JavaScript中Promise的使用詳解
Promise是ES6中的函數(shù),規(guī)范了如何處理異步任務(wù)的回調(diào)函數(shù),功能類似于jQuery的defferred。簡單說就是通過promise對象的不同狀態(tài)調(diào)用不同的回調(diào)函數(shù)。目前IE8及以下不支持,其他瀏覽器都支持。
promise對象的狀態(tài),從Pending轉(zhuǎn)換為Resolved或Rejected之后,這個(gè)promise對象的狀態(tài)就不會再發(fā)生任何變化。
使用步驟:
var promise = new Promise(function(resolve, reject) { // 異步任務(wù),通過調(diào)用resolve(value) 或 reject(error),以改變promise對象的狀態(tài);改變狀態(tài)的方法只能在此調(diào)用。 //promise狀態(tài)改變后,會調(diào)用對應(yīng)的回調(diào)方法 }); promise.then(function(value){//resolve時(shí)的回調(diào)函數(shù),參數(shù)由異步的函數(shù)傳進(jìn)來}) .catch(function(error){//發(fā)生異常時(shí)或明確reject()時(shí)的回調(diào)函數(shù)})
具體使用:
function getURL(URL) { //因?yàn)閜romise創(chuàng)建時(shí)即執(zhí)行,所以用工廠函數(shù)封裝promise對象 return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); }); } // 運(yùn)行示例 var URL = "http://httpbin.org/get"; getURL(URL).then(function onFulfilled(value){ console.log(value); }).catch(function onRejected(error){ console.error(error); });
Promise的回調(diào)只有異步方式,即使是同步任務(wù)的回調(diào)也是異步執(zhí)行 。
var promise = new Promise(function (resolve){ console.log("inner promise"); // 執(zhí)行1:同步任務(wù)先執(zhí)行 resolve(‘callBack'); }); promise.then(function(value){ console.log(value); // 執(zhí)行3:雖然注冊時(shí)狀態(tài)為resolved,但回調(diào)仍是異步的; }); console.log("outer promise"); // 執(zhí)行2:同步代碼先執(zhí)行
promise的方法鏈
then方法注冊的回調(diào)會依次被調(diào)用,每個(gè)then方法之間通過return 返回值傳遞參數(shù)。但是回調(diào)中的異常會導(dǎo)致跳過之間then的回調(diào),直接調(diào)用catch的回調(diào),之后再繼續(xù)調(diào)用剩下的then的回調(diào)。在then(onFulfilled, onRejected)中,onFulfilled的異常不會被自己的onRejected捕獲,所以優(yōu)先使用catch。
promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
taskA拋異常,taskB被跳過,finalTask仍會被調(diào)用,因?yàn)閏atch返回的promise對象的狀態(tài)為resolved。
then方法內(nèi)可以返回3種值
1. 返回另一個(gè)promise對象,下一個(gè)then方法根據(jù)其狀態(tài)選擇onFullfilled/onRejected回調(diào)函數(shù)執(zhí)行,參數(shù)仍由新promise的resolv/reject方法傳遞;
2. 返回一個(gè)同步值,下一個(gè)then方法沿用當(dāng)前promise對象的狀態(tài),無需等異步任務(wù)結(jié)束會立即執(zhí)行;實(shí)參為上一then的返回值;如果沒有return,則默認(rèn)返回undefined;
3. 拋出異常(同步/異步):throw new Error(‘xxx');
then不僅是注冊一個(gè)回調(diào)函數(shù),還會將回調(diào)函數(shù)的返回值進(jìn)行變換,創(chuàng)建并返回一個(gè)新promise對象。實(shí)際上Promise在方法鏈中的操作的都不是同一個(gè)promise對象。
var aPromise = new Promise(function (resolve) { resolve(100); }); var thenPromise = aPromise.then(function (value) { console.log(value); }); var catchPromise = thenPromise.catch(function (error) { console.error(error); }); console.log(aPromise !== thenPromise); // => true console.log(thenPromise !== catchPromise);// => true
Promise.all()靜態(tài)方法,同時(shí)進(jìn)行多個(gè)異步任務(wù)。在接收到的所有promise對象都變?yōu)镕ulFilled 或者Rejected 狀態(tài)之后才會繼續(xù)進(jìn)行后面的處理。
Promise.all([promiseA, promiseB]).then(function(results){//results是個(gè)數(shù)組,元素值和前面promises對象對應(yīng)}); // 由promise對象組成的數(shù)組會同時(shí)執(zhí)行,而不是一個(gè)一個(gè)順序執(zhí)行,開始時(shí)間基本相同。 function timerPromisefy(delay) { console.log('開始時(shí)間:”'+Date.now()) return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); Promise.all([ timerPromisefy(100), //promise用工廠形式包裝一下 timerPromisefy(200), timerPromisefy(300), timerPromisefy(400) ]).then(function (values) { console.log(values); // [100,200,300,400] });
不同時(shí)執(zhí)行,而是一個(gè)接著一個(gè)執(zhí)行promise
//promise factories返回promise對象,只有當(dāng)前異步任務(wù)結(jié)束時(shí)才執(zhí)行下一個(gè)then function sequentialize(promiseFactories) { var chain = Promise.resolve(); promiseFactories.forEach(function (promiseFactory) { chain = chain.then(promiseFactory); }); return chain; }
Promise.race()同all()類似,但是race()只要有一個(gè)promise對象進(jìn)入 FulFilled 或者 Rejected 狀態(tài)的話,就會執(zhí)行對應(yīng)的回調(diào)函數(shù)。不過在第一個(gè)promise對象變?yōu)镕ulfilled之后,并不影響其他promise對象的繼續(xù)執(zhí)行。
//沿用Promise.all()的例子 Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (value) { console.log(values); // [1] });
Promise.race()作為定時(shí)器的妙用
Promise.race([ new Promise(function (resolve, reject) { setTimeout(reject, 5000); // timeout after 5 secs }), doSomethingThatMayTakeAwhile() ]);
在then中改變promise狀態(tài)
因?yàn)閠hen的回調(diào)中只有value參數(shù),沒有改變狀態(tài)的方法(只能在構(gòu)造方法的異步任務(wù)中使用),要想改變傳給下一個(gè)then的promise對象的狀態(tài),只能重新new一個(gè)新的Promise對象,在異步任務(wù)中判斷是否改變狀態(tài),最后return出去傳給下一個(gè)then/catch。
var promise = Promise.resolve(‘xxx');//創(chuàng)建promise對象的簡介方法 promise.then(function (value) { var pms=new Promise(function(resolve,reject){ setTimeout(function () { // 在此可以判斷是否改變狀態(tài)reject/resolve Reject(‘a(chǎn)rgs'); }, 1000); }) return pms; //該promise對象可以具有新狀態(tài),下一個(gè)then/catch需要等異步結(jié)束才會執(zhí)行回調(diào);如果返回普通值/undefined,之后的then/catch會立即執(zhí)行 }).catch(function (error) { // 被reject時(shí)調(diào)用 console.log(error) });
獲取兩個(gè)promises的結(jié)果
//方法1:通過在外層的變量傳遞 var user; getUserByName('nolan').then(function (result) { user = result; return getUserAccountById(user.id); }).then(function (userAccount) { //可以訪問user和userAccount }); //方法2:后一個(gè)then方法提到前一個(gè)回調(diào)中 getUserByName('nolan').then(function (user) { return getUserAccountById(user.id).then(function (userAccount) { //可以訪問user和userAccount }); });
注意使用promise時(shí)的整體結(jié)構(gòu)
假定doSomething()和doSomethingElse()都返回了promise對象
常用方式:
doSomething().then(doSomethingElse).then(finalHandler); doSomething |-----------------| doSomethingElse(resultOfDoSomething) //返回新promise,下一個(gè)then要收到新狀態(tài)才執(zhí)行 |------------------| finalHandler(resultOfDoSomethingElse) |---------------------|
常用變通方式:
doSomething().then(function () { return doSomethingElse();}).then(finalHandler); doSomething |-----------------| doSomethingElse(undefined) //then外層函數(shù)的arguments[0]== resultOfDoSomething |------------------| finalHandler(resultOfDoSomethingElse) |------------------|
錯(cuò)誤方式1:
doSomething().then(function () { doSomethingElse();}).then(finalHandler); doSomething |-----------------| doSomethingElse(undefined) //雖然doSomethingElse會返回promise對象,但最外層的回調(diào)函數(shù)是return undefined,所以下一個(gè)then方法無需等待新promise的狀態(tài),會馬上執(zhí)行回調(diào)。 |------------------| finalHandler(undefined) |------------------|
錯(cuò)誤方式2:
doSomething().then(doSomethingElse()).then(finalHandler); doSomething |-----------------| doSomethingElse(undefined) //回調(diào)函數(shù)在注冊時(shí)就直接被調(diào)用 |----------| finalHandler(resultOfDoSomething) |------------------|
相關(guān)文章
JavaScript數(shù)組特性與實(shí)踐應(yīng)用深入詳解
這篇文章主要介紹了JavaScript數(shù)組特性與實(shí)踐應(yīng)用,較為深入而詳細(xì)的分析了javascript數(shù)組的功能、屬性、使用方法及操作注意事項(xiàng),需要的朋友可以參考下2018-12-12JS實(shí)現(xiàn)滑動拼圖驗(yàn)證功能完整示例
這篇文章主要介紹了JS實(shí)現(xiàn)滑動拼圖驗(yàn)證功能,結(jié)合完整實(shí)例形式分析了JS滑動拼圖驗(yàn)證相關(guān)原理、實(shí)現(xiàn)步驟與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03JavaScript高級程序設(shè)計(jì) 閱讀筆記(十八) js跨平臺的事件
js跨平臺的事件經(jīng)驗(yàn)分享,需要的朋友可以參考下2012-08-08