Promise 是異步編程的一種解決方案缚去,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大辜荠。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn)政冻,統(tǒng)一了用法枚抵,原生提供了Promise對象。所謂Promise明场,簡單說就是一個容器汽摹,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。從語法上說苦锨,Promise 是一個對象逼泣,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API舟舒,各種異步操作都可以用同樣的方法進(jìn)行處理拉庶。Promise對象有以下兩個特點:
1. 對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作秃励,有三種狀態(tài):pending(進(jìn)行中)氏仗、fulfilled(已成功)和rejected(已失敗)夺鲜。只有異步操作的結(jié)果皆尔,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)币励。這也是Promise這個名字的由來慷蠕,它的英語意思就是“承諾”,表示其他手段無法改變食呻。
2. 一旦狀態(tài)改變流炕,就不會再變澎现,任何時候都可以得到這個結(jié)果。Promise對象的狀態(tài)改變每辟,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected昔头。只要這兩種情況發(fā)生,狀態(tài)就凝固了影兽,不會再變了,會一直保持這個結(jié)果莱革,這時就稱為 resolved(已定型)峻堰。如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù)盅视,也會立即得到這個結(jié)果捐名。這與事件(Event)完全不同,事件的特點是闹击,如果你錯過了它镶蹋,再去監(jiān)聽,是得不到結(jié)果的赏半。
--來自官方文檔
Javascript是一種單線程的語言贺归,所有的代碼必須按照所謂的“自上而下”的順序來執(zhí)行。本特性帶來的問題就是断箫,一些將來的拂酣、未知的操作,必須異步實現(xiàn)仲义。之前我們想要執(zhí)行一些比較復(fù)雜的異步操作婶熬,就會嵌套很多層回調(diào)函數(shù),引入ES6中Promise這個概念埃撵,處理起來就比較簡單了赵颅。
//原始方法
async1(function(){
console.log('async1執(zhí)行完畢');
async2(function(){
console.log('async2執(zhí)行完畢');
async3(function(
console.log('async3執(zhí)行完畢');
async4(funciton(){
console.log('async4執(zhí)行完畢');
async5(function(){
console.log('async5執(zhí)行完畢');
console.log('流程執(zhí)行完畢');
});
});
));
});
});
是不是看迷糊了,在真正的項目開發(fā)中也許會遇到更加復(fù)雜的執(zhí)行情況暂刘。
//ES6-Promise
Promise.then(() => {
console.log('async1執(zhí)行完畢');
}).then(() => {
console.log('async2執(zhí)行完畢');
}).then(() => {
console.log('async3執(zhí)行完畢');
}).then(() => {
console.log('async4執(zhí)行完畢');
}).then(() => {
console.log('async5執(zhí)行完畢');
console.log('流程執(zhí)行完畢');
});
這樣看是不是清晰多了饺谬。
ES6-Promise中有以下幾個操作方法,鏈?zhǔn)讲僮鳌?code>resolve鸳惯、reject
商蕴、catch
、all
芝发、race
绪商。
一、鏈?zhǔn)讲僮?/h6>
從表面上看辅鲸,Promise只是能夠簡化層層回調(diào)的寫法格郁,而實質(zhì)上,Promise的精髓是“狀態(tài)”,用維護(hù)狀態(tài)例书、傳遞狀態(tài)的方式來使得回調(diào)函數(shù)能夠及時調(diào)用锣尉,它比傳遞callback函數(shù)要簡單、靈活的多决采。
let runAsync1 = () => {
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('異步任務(wù)1執(zhí)行完成');
resolve('數(shù)據(jù)1');
}, 1000);
});
return p;
}
let runAsync2 = () =>{
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('異步任務(wù)2執(zhí)行完成');
resolve('數(shù)據(jù)2');
}, 2000);
});
return p;
}
let runAsync3 = () =>{
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('異步任務(wù)3執(zhí)行完成');
resolve('數(shù)據(jù)3');
}, 2000);
});
return p;
}
runAsync1().then((data) => {
console.log(data);
return runAsync2();
}).then((data) => {
console.log(data);
return runAsync3();
}).then((data) => {
console.log(data);
});
這樣能夠按順序自沧,每隔兩秒輸出每個異步回調(diào)中的內(nèi)容,在runAsync2中傳給resolve的數(shù)據(jù)树瞭,能在接下來的then方法中拿到拇厢。以上代碼的執(zhí)行結(jié)果為:而我們更改調(diào)用順序,并在最后一步晒喷,返回‘執(zhí)行結(jié)束’字符串孝偎。
runAsync1().then((data) => {
console.log(data);
return runAsync3();
}).then((data) => {
console.log(data);
return runAsync2();
}).then((data) => {
console.log(data);
return '執(zhí)行結(jié)束';
}).then((data) => {
console.log(data);
});
執(zhí)行結(jié)果如下:這樣我們就可以很方便、清晰的去決定我們的程序的執(zhí)行順序凉敲,達(dá)到預(yù)計想要的結(jié)果衣盾。
二、resolve 操作
resolve的作用就是把Promise的狀態(tài)置為resolved爷抓,這樣我們在then中就能捕捉到势决,然后執(zhí)行“成功”情況的回調(diào)。這里就不做細(xì)展示了废赞,在之前的例子中都可以看到詳細(xì)的代碼結(jié)構(gòu)徽龟。
三、reject 操作
reject和resolve類似唉地,reject的作用就是把Promise的狀態(tài)置為rejected据悔,這樣我們在then中就能捕捉到,然后執(zhí)行“失敗”情況的回調(diào)耘沼。但是resolve是必須的參數(shù)极颓,reject不是必須的。
let compare = (num1,num2) => {
var p = new Promise((resolve, reject) => {
if (num1 < num2) {
resolve('resolved:'+num1+'<'+num2);
} else if (num1 === num2) {
resolve('resolved:'+num1+'='+num2);
} else {
reject('rejected:'+num1+'>'+num2);
}
});
return p;
}
compare(7,10).then((data) => {
console.log(data);
},(err) => {
console.log(err);
});
compare(10,10).then((data) => {
console.log(data);
},(err) => {
console.log(err);
});
compare(10,7).then((data) => {
console.log(data);
},(err) => {
console.log(err);
});
如以上程序如果第一個參數(shù)小于等于第二個參數(shù)群嗤,則Promise的狀態(tài)為成功菠隆;如果第一個參數(shù)大于第二個參數(shù),則Promise的狀態(tài)為失敗狂秘。如圖:四骇径、catch 操作
catch的一個作用與reject類似。如:
compare(10,7).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
結(jié)果:在執(zhí)行resolve的回調(diào)時者春,如果拋出異常了(代碼出錯了)破衔,那么就會報錯卡死js。如:
compare(10,7).then((data) => {
console.log(data);
console.log(msg);
},(err) => {
console.log(err);
})
執(zhí)行結(jié)果:而catch的另一個作用就是在執(zhí)行resolve的回調(diào)時钱烟,如果拋出異常了(代碼出錯了)晰筛,js不會報錯卡死嫡丙,而是會進(jìn)到這個catch方法中。如:
compare(7,10).then((data) => {
console.log(data);
console.log(msg);
}).catch((err) => {
console.log(err);
});
執(zhí)行結(jié)果:五读第、all 操作
all方法提供了并行執(zhí)行異步操作的能力曙博,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。all里的所有個異步操作的并行執(zhí)行的怜瞒,等到它們都執(zhí)行完后才會進(jìn)到then里面父泳,在then里面我們會接受到一個數(shù)組就是all的異步操作依次得到的值。如:
Promise.all([
runAsync1(), runAsync2(), runAsync3()
]).then(function(data){
console.log(data);
});
執(zhí)行結(jié)果:六吴汪、race 操作
all方法的執(zhí)行原理是所有異步操作都執(zhí)行完才會進(jìn)入then操作尘吗,也就是要等最慢的一個執(zhí)行完才會進(jìn)入then操作;而race方法的執(zhí)行原理則是最快的一個異步操作執(zhí)行完就立即進(jìn)入then操作浇坐,其他異步操作會繼續(xù)進(jìn)行但不會進(jìn)入then操作。如下:
Promise.race([
runAsync1(), runAsync2(), runAsync3()
]).then(function(results){
console.log(results);
});
執(zhí)行結(jié)果:只有不斷找尋機(jī)會的人才會及時把握機(jī)會黔宛。