JavaScript中Promise的使用詳解
Promise是ES6中的函數(shù),規(guī)范了如何處理異步任務(wù)的回調(diào)函數(shù),功能類似于jQuery的defferred。簡(jiǎn)單說(shuō)就是通過(guò)promise對(duì)象的不同狀態(tài)調(diào)用不同的回調(diào)函數(shù)。目前IE8及以下不支持,其他瀏覽器都支持。
promise對(duì)象的狀態(tài),從Pending轉(zhuǎn)換為Resolved或Rejected之后,這個(gè)promise對(duì)象的狀態(tài)就不會(huì)再發(fā)生任何變化。
使用步驟:
var promise = new Promise(function(resolve, reject) {
// 異步任務(wù),通過(guò)調(diào)用resolve(value) 或 reject(error),以改變promise對(duì)象的狀態(tài);改變狀態(tài)的方法只能在此調(diào)用。
//promise狀態(tài)改變后,會(huì)調(diào)用對(duì)應(yīng)的回調(diào)方法
});
promise.then(function(value){//resolve時(shí)的回調(diào)函數(shù),參數(shù)由異步的函數(shù)傳進(jìn)來(lái)})
.catch(function(error){//發(fā)生異常時(shí)或明確reject()時(shí)的回調(diào)函數(shù)})
具體使用:
function getURL(URL) { //因?yàn)閜romise創(chuàng)建時(shí)即執(zhí)行,所以用工廠函數(shù)封裝promise對(duì)象
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:雖然注冊(cè)時(shí)狀態(tài)為resolved,但回調(diào)仍是異步的;
});
console.log("outer promise"); // 執(zhí)行2:同步代碼先執(zhí)行
promise的方法鏈
then方法注冊(cè)的回調(diào)會(huì)依次被調(diào)用,每個(gè)then方法之間通過(guò)return 返回值傳遞參數(shù)。但是回調(diào)中的異常會(huì)導(dǎo)致跳過(guò)之間then的回調(diào),直接調(diào)用catch的回調(diào),之后再繼續(xù)調(diào)用剩下的then的回調(diào)。在then(onFulfilled, onRejected)中,onFulfilled的異常不會(huì)被自己的onRejected捕獲,所以優(yōu)先使用catch。
promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
taskA拋異常,taskB被跳過(guò),finalTask仍會(huì)被調(diào)用,因?yàn)閏atch返回的promise對(duì)象的狀態(tài)為resolved。
then方法內(nèi)可以返回3種值
1. 返回另一個(gè)promise對(duì)象,下一個(gè)then方法根據(jù)其狀態(tài)選擇onFullfilled/onRejected回調(diào)函數(shù)執(zhí)行,參數(shù)仍由新promise的resolv/reject方法傳遞;
2. 返回一個(gè)同步值,下一個(gè)then方法沿用當(dāng)前promise對(duì)象的狀態(tài),無(wú)需等異步任務(wù)結(jié)束會(huì)立即執(zhí)行;實(shí)參為上一then的返回值;如果沒(méi)有return,則默認(rèn)返回undefined;
3. 拋出異常(同步/異步):throw new Error(‘xxx');
then不僅是注冊(cè)一個(gè)回調(diào)函數(shù),還會(huì)將回調(diào)函數(shù)的返回值進(jìn)行變換,創(chuàng)建并返回一個(gè)新promise對(duì)象。實(shí)際上Promise在方法鏈中的操作的都不是同一個(gè)promise對(duì)象。
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對(duì)象都變?yōu)镕ulFilled 或者Rejected 狀態(tài)之后才會(huì)繼續(xù)進(jìn)行后面的處理。
Promise.all([promiseA, promiseB]).then(function(results){//results是個(gè)數(shù)組,元素值和前面promises對(duì)象對(duì)應(yīng)});
// 由promise對(duì)象組成的數(shù)組會(huì)同時(shí)執(zhí)行,而不是一個(gè)一個(gè)順序執(zhí)行,開(kāi)始時(shí)間基本相同。
function timerPromisefy(delay) {
console.log('開(kāi)始時(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對(duì)象,只有當(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對(duì)象進(jìn)入 FulFilled 或者 Rejected 狀態(tài)的話,就會(huì)執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。不過(guò)在第一個(gè)promise對(duì)象變?yōu)镕ulfilled之后,并不影響其他promise對(duì)象的繼續(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ù),沒(méi)有改變狀態(tài)的方法(只能在構(gòu)造方法的異步任務(wù)中使用),要想改變傳給下一個(gè)then的promise對(duì)象的狀態(tài),只能重新new一個(gè)新的Promise對(duì)象,在異步任務(wù)中判斷是否改變狀態(tài),最后return出去傳給下一個(gè)then/catch。
var promise = Promise.resolve(‘xxx');//創(chuàng)建promise對(duì)象的簡(jiǎn)介方法
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對(duì)象可以具有新?tīng)顟B(tài),下一個(gè)then/catch需要等異步結(jié)束才會(huì)執(zhí)行回調(diào);如果返回普通值/undefined,之后的then/catch會(huì)立即執(zhí)行
}).catch(function (error) {
// 被reject時(shí)調(diào)用
console.log(error)
});
獲取兩個(gè)promises的結(jié)果
//方法1:通過(guò)在外層的變量傳遞
var user;
getUserByName('nolan').then(function (result) {
user = result;
return getUserAccountById(user.id);
}).then(function (userAccount) {
//可以訪問(wèn)user和userAccount
});
//方法2:后一個(gè)then方法提到前一個(gè)回調(diào)中
getUserByName('nolan').then(function (user) {
return getUserAccountById(user.id).then(function (userAccount) {
//可以訪問(wèn)user和userAccount
});
});
注意使用promise時(shí)的整體結(jié)構(gòu)
假定doSomething()和doSomethingElse()都返回了promise對(duì)象
常用方式:
doSomething().then(doSomethingElse).then(finalHandler);
doSomething
|-----------------|
doSomethingElse(resultOfDoSomething) //返回新promise,下一個(gè)then要收到新?tīng)顟B(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會(huì)返回promise對(duì)象,但最外層的回調(diào)函數(shù)是return undefined,所以下一個(gè)then方法無(wú)需等待新promise的狀態(tài),會(huì)馬上執(zhí)行回調(diào)。
|------------------|
finalHandler(undefined)
|------------------|
錯(cuò)誤方式2:
doSomething().then(doSomethingElse()).then(finalHandler);
doSomething
|-----------------|
doSomethingElse(undefined) //回調(diào)函數(shù)在注冊(cè)時(shí)就直接被調(diào)用
|----------|
finalHandler(resultOfDoSomething)
|------------------|
相關(guān)文章
原生js實(shí)現(xiàn)簡(jiǎn)單的Ripple按鈕實(shí)例代碼
本篇文章主要介紹了原生js實(shí)現(xiàn)簡(jiǎn)單的Ripple按鈕實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
js獲取input標(biāo)簽的輸入值實(shí)現(xiàn)代碼
input標(biāo)簽的輸入值通過(guò)js進(jìn)行獲取,部分標(biāo)簽和類是封裝在框架內(nèi)的,其效果和html標(biāo)簽差不多,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下,希望對(duì)大家有所幫助2013-08-08
JS解決移動(dòng)web開(kāi)發(fā)手機(jī)輸入框彈出的問(wèn)題
在移動(dòng)web開(kāi)發(fā)中和pc端不同的是,手機(jī)的輸入是軟鍵盤,這樣就會(huì)有個(gè)問(wèn)題,那就是當(dāng)有輸入的時(shí)候,鍵盤彈起來(lái),整個(gè)頁(yè)面難免會(huì)發(fā)生變化。怎么解決這個(gè)問(wèn)題呢?下面腳本之家小編給大家分享JS解決移動(dòng)web開(kāi)發(fā)手機(jī)輸入框彈出的問(wèn)題2017-03-03
JS中filter( )數(shù)組過(guò)濾器的使用
這篇文章主要介紹了filter() 數(shù)組過(guò)濾的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11
簡(jiǎn)易的JS計(jì)算器實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了JS簡(jiǎn)易的計(jì)算器實(shí)現(xiàn)代碼,,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
JavaScript實(shí)現(xiàn)為指定對(duì)象添加多個(gè)事件處理程序的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)為指定對(duì)象添加多個(gè)事件處理程序的方法,可實(shí)現(xiàn)讓指定對(duì)象處理多個(gè)事件的功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
Js實(shí)現(xiàn)當(dāng)前點(diǎn)擊a標(biāo)簽變色突出顯示其他a標(biāo)簽回復(fù)原色
當(dāng)一個(gè)頁(yè)面有多個(gè)a標(biāo)簽,實(shí)現(xiàn)當(dāng)前點(diǎn)擊a標(biāo)簽變色,其他a標(biāo)簽回復(fù)原色,具體實(shí)現(xiàn)如下,喜歡的朋友可以參考下2013-11-11

