JavaScript中的Promise詳解
前言
Promise是ES6引入的異步編程的新解決方案。語法上Promise是一個(gè)構(gòu)造函數(shù),用來封裝異步操作并可以獲取其成功或失敗的結(jié)果。
Promise特點(diǎn):
Promsie對(duì)象異步操作有三種狀態(tài),pending(進(jìn)行中)、fulfilled(已成功)和reject(已失敗)。只有異步操作才可以決定當(dāng)前是哪種狀態(tài);Promise狀態(tài)改變有兩種可能,從pending變?yōu)閒ulfidded和從pending變?yōu)閞ejected。狀態(tài)發(fā)生改變就不能再改變了,稱為:resolved(已定型)
Promise對(duì)象的作用:將異步操作以同步操作的流程表達(dá)出來,避免層層嵌套的回調(diào)函數(shù),而且Promise提供了統(tǒng)一的接口,使得控制異步操作更加容易。
Promise使用
Promise構(gòu)造函數(shù)接收一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別為:resolve 和 reject。
resolve:在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果作為參數(shù)傳遞出去。
reject:在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤作為參數(shù)傳遞出去。
Promise實(shí)例生成后,可以用 then 方法分別指定 resolved 狀態(tài)和 rejected 狀態(tài)的回調(diào)函數(shù);而第一個(gè)回調(diào)函數(shù)是 Promise 對(duì)象狀態(tài)變 resolved 時(shí)調(diào)用,第二個(gè)回調(diào)函數(shù)是 Promise 對(duì)象的狀態(tài)變?yōu)?rejected 時(shí)調(diào)用。案例如下:
<script> // 實(shí)例化 Promise 對(duì)象,接收參數(shù)為函數(shù)類型值 const p = new Promise(function(resolve,reject){ // 封裝異步操作 setTimeout(function(){ // 數(shù)據(jù)操作 // let data = '用戶數(shù)據(jù)' // resolve // resolve(data) let err = '數(shù)據(jù)讀取失敗' reject(err) },1000) }) // 調(diào)用 Promise 對(duì)象的 then 方法 // 數(shù)據(jù)調(diào)用成功,則調(diào)用下面第一個(gè)回調(diào),失敗則是第二個(gè) p.then(function(value){//成功的形參 console.log(value); },function(reason){//失敗的形參 console.log(reason); }) </script>
Promise新建后會(huì)立即執(zhí)行,然而 then 方法指定的回調(diào)函數(shù)將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行。
<script> let p = new Promise(function(resolve,reject){ console.log('People'); // 數(shù)據(jù)操作 let data = 'World' resolve(data) }) p.then(function(value){ console.log(value); }) console.log('Hello'); </script>
Promise封裝Ajax請(qǐng)求
<script> const p = new Promise((resolve,reject) =>{ // 1.創(chuàng)建對(duì)象 const xhr = new XMLHttpRequest() // 2.初始化 xhr.open("GET","https://ai.baidu.com/") // 3.發(fā)送 xhr.send() // 4.綁定事件,處理響應(yīng)結(jié)果 xhr.onreadystatechange = function(){ // 判斷 if(xhr.readyState ===4 ){ // 判斷響應(yīng)碼 200-299 if(xhr.status >= 200 && xhr.status <300){ // 表示成功 resolve(xhr.response); }else{ // 如果失敗 reject(xhr.status); } } } }) // 指定成功和失敗的回調(diào) p.then(function(value){ console.log(value); },function(reason){ console.error(reason); }) </script>
Promise封裝讀取文件
這里借助 Node.js 方法進(jìn)行讀取文件,不了解Node.js的方法可以關(guān)注我,后期會(huì)出相關(guān)專欄。
// 1.引入 fs 模塊 const fs = require('fs') // 2.調(diào)用方法讀取文件 // fs.readFile('./index.md',(err,data)=>{ // // 如果失敗,則拋出錯(cuò)誤 // if(err) throw err; // // 如果沒有出錯(cuò),則輸出內(nèi)容 // console.log(data.toString()); // }) // 3.使用 Promise 封裝 const p = new Promise(function(resolve,reject){ fs.readFile('./index.md',(err,data)=>{ // 判斷如果失敗 if(err) reject(err) // 如果成功 resolve(data) }) }) p.then(function(value){ console.log(value.toString()); },function(reason){ console.log("讀取失敗!!"); })
Promise.prototype.then方法
Promise實(shí)例具有then方法,即then方法定義在原型對(duì)象Promise.prototype上,作用是為Promise實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù)。案例如下:
<script> // 創(chuàng)建 promise 兌現(xiàn) const p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('用戶數(shù)據(jù)') // reject('出錯(cuò)啦') }) }) // 調(diào)用 then 方法 const result = p.then(value =>{ // 如果回調(diào)函數(shù)中返回的結(jié)果是非 promise 類型屬性,狀態(tài)為成功,返回值為對(duì)象的成功的值 console.log(value); // 1、非promise類型的屬性 // return 123 // 2、是promise對(duì)象 // return new Promise((resolve,reject)=>{ // // resolve('ok') // // reject('error') // }) // 3、拋出錯(cuò)誤 throw new Error('出錯(cuò)啦!!') },reason=>{ console.log(reason); }) // then方法的返回結(jié)果是 Promise 對(duì)象,對(duì)象狀態(tài)由回調(diào)函數(shù)的執(zhí)行結(jié)果決定 console.log(result); </script>
then方法返回的是一個(gè)新的Promise實(shí)例,因此可以采用鏈?zhǔn)綄懛?,即then方法后面再調(diào)用另一個(gè)then方法。但是前一個(gè)then()方法中的回調(diào)函數(shù)中又可能返回一個(gè)Promise實(shí)例,這時(shí)候后面一個(gè)then()方法中的回調(diào)函數(shù)會(huì)等前一個(gè)Promise實(shí)例狀態(tài)發(fā)生變化才會(huì)調(diào)用。案例如下:
<script> let p = new Promise((resolve, reject) => { setTimeout(()=>{ resolve('success') }); },1000); p.then( res => { console.log(res); return `${res} again`; } ) .then( res => console.log(res) ); </script>
Promise多文件讀取
回調(diào)地獄與Promise對(duì)象實(shí)現(xiàn)相比,不會(huì)產(chǎn)生回調(diào)現(xiàn)象,而且也不用再數(shù)據(jù)龐大時(shí)進(jìn)行大規(guī)模的縮進(jìn)。承接上文單文件讀取,現(xiàn)在進(jìn)行多文件讀取,案例如下:
// 1.引入 fs 模塊 const { rejects } = require('assert') const fs = require('fs') const { resolve } = require('path') // 2.回調(diào)地獄 調(diào)用方法讀取文件 // fs.readFile('./index.md',(err,data)=>{ // fs.readFile('./index1.md',(err,data1)=>{ // fs.readFile('./index2.md',(err,data2)=>{ // let result = data + '\r\n' + data1 +'\r\n'+ data2 // console.log(result); // }) // }) // }) // 3.使用 Promise 實(shí)現(xiàn) const p = new Promise((resolve,reject)=>{ fs.readFile('./index.md',(err,data)=>{ resolve(data) }) }) // value 是第一個(gè)文件的內(nèi)容 p.then(value => { return new Promise((resolve,reject)=>{ fs.readFile('./index1.md',(err,data)=>{//data是第二個(gè)文件的內(nèi)容 // 返回的是第一個(gè)和第二個(gè)文件合并的數(shù)組 resolve([value, data]) }) }) }).then(value => {//這里的value就是上面合并的數(shù)組 return new Promise((resolve,reject)=>{ fs.readFile('./index2.md',(err,data)=>{//data是第三個(gè)文件的內(nèi)容 // 壓入 value.push(data) resolve(value) }) }) }).then(value => {//如果上面成功,現(xiàn)在的value就是返回三個(gè)數(shù)組的合集 console.log(value.join('\r\n'));//數(shù)組用join進(jìn)行拼接 })
Promise.prototype.catch()
該方法用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。舉個(gè)簡單的例子:
<script> const p = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('出錯(cuò)了!') },1000) }) // p.then(value=>{},reason=>{ // console.log(reason); // }) p.catch(reason=>{ console.log(reason); }) </script>
Promise.prototype.finally()
finally()方法指定不管promise最后的狀態(tài)如何,在執(zhí)行完then或catch指定的回調(diào)函數(shù)以后,都會(huì)執(zhí)行finally方法指定的回調(diào)函數(shù)。
<script> const p = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('出錯(cuò)了!') },1000) }) p.catch(reason=>{ console.log(reason); }).finally(()=>{ console.log('我是finall,不管promise結(jié)果如何我都要執(zhí)行'); }) </script>
Promise.all()
該方法用于將多個(gè) Promise 實(shí)例包裝成一個(gè)新的 Promise 實(shí)例,方法接受一個(gè)數(shù)組作為參數(shù),數(shù)組參數(shù)都是Promsie實(shí)例。當(dāng)然參數(shù)也可以不是數(shù)組,但必須有Iterator接口,且返回的每個(gè)成員都是Promise實(shí)例。
該方法只適合所有異步都操作成功的情況,如果有一個(gè)操作失敗就無法滿足要求。
<script> // Promise.all()的狀態(tài)由參數(shù)決定:分以下兩種情況 /* * (1)Promise.all()參數(shù)的狀態(tài)都變成fulfilled,Promise.all()狀態(tài)才會(huì)變成fulfilled,此時(shí)所有參數(shù)的返回值組成一個(gè)數(shù)組,傳遞給Promise.all()的回調(diào)函數(shù)。 * (2)只要參數(shù)之中有一個(gè)被rejected,Promise.all()的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值,會(huì)傳遞給p的回調(diào)函數(shù)。 */ const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result); const p2 = new Promise((resolve, reject) => { throw new Error('報(bào)錯(cuò)了'); }) .then(result => result); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e));//Error:報(bào)錯(cuò)了 </script>
Promise.race()
該方法同樣是將多個(gè) Promise 實(shí)例包裝成一個(gè)新的 Promsie 實(shí)例,該方法與 Promise.all()方法一樣,區(qū)別是該方法中只要參數(shù)之中有一個(gè)實(shí)例率先改變狀態(tài),該方法的實(shí)例狀態(tài)跟著改變,那個(gè)率先改變的 Promise 實(shí)例的返回值就傳遞給該方法實(shí)例的回調(diào)函數(shù)。
<script> const p = Promise.race([ fetch('./index.js'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); p.then(console.log).catch(console.error); </script>
上面代碼中,如果 5 秒之內(nèi)fetch方法無法返回結(jié)果,變量p的狀態(tài)就會(huì)變成rejected,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。
Promise.allSettled()
該方法用來確定一組異步是否都結(jié)束(不管成功或失敗)。方法接受一個(gè)數(shù)組作為參數(shù),只有當(dāng)參數(shù)數(shù)組中所有 Promise對(duì)象 都發(fā)生變化,返回的 Promise 對(duì)象才會(huì)發(fā)生狀態(tài)變更。
<script> const resolved = Promise.resolve(42); const rejected = Promise.reject(-1); const allSettledPromise = Promise.allSettled([resolved, rejected]); allSettledPromise.then(function (results) { console.log(results); }); </script>
回調(diào)函數(shù)接受到的參數(shù)是數(shù)組results,該數(shù)組的每一個(gè)成員都是一個(gè)對(duì)象,對(duì)應(yīng)傳入Promise.allSettled()的數(shù)組里面的兩個(gè) Promsie 對(duì)象。
Pomise.any()
Promise.any()和Promise.race()方法很像,唯一區(qū)別就是Promise.any()不會(huì)因?yàn)槟硞€(gè) Promise 變成 rejected 狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise 變成 rejected 狀態(tài)才會(huì)結(jié)束。
var resolved = Promise.resolve(42); var rejected = Promise.reject(-1); var alsoRejected = Promise.reject(Infinity); Promise.any([resolved, rejected, alsoRejected]).then(function (result) { console.log(result); // 42 }); Promise.any([rejected, alsoRejected]).catch(function (results) { console.log(results instanceof AggregateError); // true console.log(results.errors); // [-1, Infinity] });
Promise.resolve()
該方法能夠?qū)F(xiàn)有對(duì)象轉(zhuǎn)換為 Promise 對(duì)象。
Promise.resolve('foo') // 等價(jià)于 new Promise(resolve => resolve('foo'))
到此這篇關(guān)于JavaScript中的Promise詳解 的文章就介紹到這了,更多相關(guān)JS Promise內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)數(shù)組在指定位置插入若干元素的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)數(shù)組在指定位置插入若干元素的方法,涉及javascript中splice方法的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04淺析Javascript中雙等號(hào)(==)隱性轉(zhuǎn)換機(jī)制
這篇文章給大家詳細(xì)介紹了javascript中雙等號(hào)(==)隱性轉(zhuǎn)換機(jī)制,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-10-105個(gè)書寫JavaScript代碼的壞習(xí)慣,看看你中槍了沒?
這篇文章主要介紹了5個(gè)書寫JavaScript代碼的壞習(xí)慣,看看你中槍了沒?,本文指出了你沒有使用命名空間、變量定義的東一個(gè)西一個(gè)、Javascript的變量范圍、Javascript的面向?qū)ο?、new關(guān)鍵字等問題,需要的朋友可以參考下2014-11-11整理JavaScript創(chuàng)建對(duì)象的八種方法
JavaScript創(chuàng)建對(duì)象的方法有很多種,本文給大家介紹javascript創(chuàng)建對(duì)象的八種方法,對(duì)javascript創(chuàng)建對(duì)象感興趣的朋友可以參考下本篇文章2015-11-11ie6下png圖片背景不透明的解決辦法使用js實(shí)現(xiàn)
我們時(shí)常在使用png圖片的時(shí)候,在ie6下發(fā)生背景不透明的問題,解決的方法實(shí)在是太多了,下面給大家介紹下一個(gè)js解決的方式,感興趣的朋友可以了解下的2013-01-01