Promise提供了一種異步執(zhí)行模式航徙。
注意一點(diǎn)Promise的執(zhí)行仍然是異步方式的谈况,并沒(méi)有改變成同步執(zhí)行模式嫩海,只不過(guò)讓代碼寫(xiě)起來(lái)讀起來(lái)像是同步執(zhí)行一樣。
Promise是使用主要分兩步:
第一步谣拣,定義Promise
var p = new Promise((resolve, reject) => {
//
// promise body
// if (...) {
// resolve(value)
// } else {
// reject(reason)
// }
//
});
注意3點(diǎn):
- 定義Promise時(shí)需要一個(gè)參數(shù)募寨,這個(gè)參數(shù)是一個(gè)函數(shù),我把它叫做Promise函數(shù)體
function (resolve, reject) { ... }
- Promise函數(shù)體接受兩個(gè)參數(shù)
// 參數(shù)resolve和reject也是一個(gè)函數(shù)芝发,原型定義如下:
// function resolve(value) {...}
// function reject(reason) {...}
- Promise函數(shù)體不需要返回值绪商。
Promise函數(shù)體的返回是通過(guò)回調(diào)參數(shù)函數(shù)resolve和reject來(lái)標(biāo)記函數(shù)成功失敗辅鲸;如果成功則調(diào)用resolve()函數(shù)格郁,如果失敗則調(diào)用reject()函數(shù)。
請(qǐng)比較Promise函數(shù)體原型独悴,和Promise.then函數(shù)體原型例书,他們是一樣的,他們應(yīng)該就是一樣的刻炒。
第二步决采,定義resolve和reject參數(shù)函數(shù)
前面Promise函數(shù)體里面用到了resolve和reject函數(shù),但彼時(shí)只是形參而已坟奥,這兩個(gè)函數(shù)并沒(méi)有被定義出來(lái)树瞭;那么他們?cè)谀睦锒x的呢,Promise的then和catch函數(shù)就是真正用來(lái)定義resolve和reject函數(shù)體的爱谁。
函數(shù)then和catch本身是Promise的一個(gè)內(nèi)置函數(shù)(注意區(qū)分兩個(gè)概念:then函數(shù)晒喷,和then函數(shù)的參數(shù)函數(shù)):
promise.then(function(value) {
// body
}).catch(function(reason) {
// body
})
then和catch函數(shù)都接收一個(gè)函數(shù)作為參數(shù),這個(gè)參數(shù)函數(shù)原型定義
function (value) { ... }
我們可以看到這個(gè)函數(shù)原型定義和Promise定義時(shí)的參數(shù)resolve和reject的原型是一樣的访敌,這就明白了吧凉敲。
- promise.then()函數(shù)的返回值是一個(gè)Promise對(duì)象,這是為了構(gòu)造then()函數(shù)鏈。
promise.catch()函數(shù)的返回值也是一個(gè)Promise對(duì)象(盡管我們通常不會(huì)使用這個(gè)對(duì)象)爷抓。 - then()函數(shù)參數(shù)函數(shù)的返回值势决,是作為下一個(gè)then函數(shù)鏈的輸入?yún)?shù)。
如果返回值是一個(gè)Promise對(duì)象除外蓝撇,后面有分析果复。 - 這里要弄清楚then()函數(shù)的返回值,和then函數(shù)參數(shù)函數(shù)的返回值唉地。
3.1 then函數(shù)的定義是Promise內(nèi)置定義的据悔,它的代碼传透,輸入輸出都由Promise實(shí)現(xiàn)耘沼,不是用戶(hù)設(shè)置的;也就是用戶(hù)根本看不見(jiàn)then函數(shù)的返回語(yǔ)句朱盐。
3.2 then函數(shù)參數(shù)函數(shù)是傳遞給then()的那個(gè)用戶(hù)定義函數(shù)群嗤,這個(gè)函數(shù)體是由用戶(hù)定義的,所以其返回值都是由用戶(hù)提供的兵琳,如下代碼可以返回一個(gè)數(shù)字狂秘,字符串,或者對(duì)象都可以躯肌。
1 function foo(param) {
2 var p = new Promise((resolve, reject) => {
3 if (param == 1) {
4 resolve(param)
5 }
6 else {
7 reject("invalid value " + param)
8 }
9 });
10 return p
11 }
12
13 foo(1)
14 .then((value) => {
15 console.log("then-1: " + value);
16 return "2"
17 })
18 .then((value) => {
19 console.log("then-2: " + value);
20 })
21 .catch((reason) => {
22 console.log("catch: " + reason);
23 })
運(yùn)行結(jié)果:
$ node p.js
then-1: 1
then-2: 2
- 第15行then-1的輸出 value是第4行resolve函數(shù)的參數(shù)值者春。
- 第19行then-2的輸出 value是第16行return的值,即前一個(gè)then函數(shù)體的返回值作為下一個(gè)then函數(shù)體的參數(shù)清女。
我們看到在then()鏈中钱烟,一個(gè)then函數(shù)體里的返回值是作為下一個(gè)then函數(shù)體的參數(shù)使用的,那么如何在一個(gè)then函數(shù)體里面標(biāo)記一個(gè)reject狀態(tài)呢嫡丙,因此此時(shí)也不是一個(gè)Promise定義函數(shù)拴袭,也就沒(méi)有reject函數(shù)形參可用。
- 辦法1: 也是最直白的辦法是拋出異常曙博,還是以上面代碼為例:
...
13 foo(1)
14 .then((value) => {
15 console.log("then-1: " + value);
16 throw "2"
17 })
18 .then((value) => {
19 console.log("then-2: " + value);
20 })
21 .catch((reason) => {
22 console.log("catch: " + reason);
23 })
我們把第16行的return語(yǔ)句改成了throw語(yǔ)句拥刻,運(yùn)行結(jié)果:
$ node p.js
then-1: 1
catch: 2
- 辦法2:再定義一個(gè)Promise并返回
13 foo(1)
14 .then((value) => {
15 console.log("then-1: " + value);
16 return new Promise((resolve, reject) => {
17 if (value == 1) {
18 console.log("then-1-1: " + value);
19 resolve(100)
20 }
21 else {
22 reject("invalid value " + value)
23 }
24 });
25 })
26 .then((value) => {
27 console.log("then-2: " + value);
28 })
29 .catch((reason) => {
30 console.log("catch: " + reason);
31 })
在then-1里面可以返回一個(gè)值給then-2使用,或者拋出一個(gè)異常直接到catch分支父泳,也可以定義一個(gè)Promise對(duì)象般哼,在新定義的Promise函數(shù)體里面使用resolve或者reject以決定走then-2還是catch異常分支。
$ node p.js
then-1: 1
then-1-1: 1
then-2: 100
- 第19行resolve的結(jié)果是吧值100傳給then-2作為參數(shù)惠窄。
- 注意這里then函數(shù)參數(shù)函數(shù)里面返回一個(gè)具體值和返回一個(gè)Promise對(duì)象的差異
- 如果返回一個(gè)普通值蒸眠,則把值直接傳給后面的一個(gè)then作為參數(shù)。
- 如果返回一個(gè)Promise對(duì)象睬捶,則根據(jù)promise的resolve和reject決定后面的then參數(shù)值黔宛。
補(bǔ)充一點(diǎn),其實(shí)then()函數(shù)的原型有兩個(gè)參數(shù)
po.then(function(value) { // success },
function(value) { // failure }
);
分別對(duì)應(yīng)Promise最終狀態(tài)是resolve和reject,而通常為了構(gòu)造Promise鏈才只提供一個(gè)參數(shù)函數(shù)臀晃,即resolve參數(shù)觉渴,而把所有的reject函數(shù)參數(shù)統(tǒng)一到catch函數(shù)里面。
Promise相關(guān)代碼的執(zhí)行順序
看一個(gè)例子來(lái)說(shuō)明問(wèn)題徽惋,
console.log("main-1");
function foo(param) {
var p = new Promise((resolve, reject) => {
console.log("promise-1");
if (param == 1) {
resolve("1")
} else {
reject("2")
}
console.log("promise-2");
});
return p
}
console.log("main-2");
foo(1).then((value) => {
console.log("then-1");
}).catch((reason) => {
console.log("catch-1");
})
console.log("main-3");
執(zhí)行結(jié)果如下:
$ node promise.js
main-1
main-2
promise-1
promise-2
main-3
then-1
所以
- Promise函數(shù)體是在Promise對(duì)象創(chuàng)建的時(shí)候就被執(zhí)行了案淋,可以理解為Promise函數(shù)是同步執(zhí)行的。
由此我們知道resolve或者reject函數(shù)已經(jīng)在Promise函數(shù)體內(nèi)被調(diào)用了险绘,而此時(shí)resolve和reject的值并沒(méi)有被定義了踢京,怎么辦?其實(shí)這就是Promise機(jī)制實(shí)現(xiàn)的功能宦棺,可是先調(diào)用一個(gè)未定義的函數(shù)瓣距,等將來(lái)函數(shù)被定義的時(shí)候(then())在真正執(zhí)行函數(shù)體。 - then/catch函數(shù)體并不是在then/catch被調(diào)用的時(shí)候執(zhí)行的代咸,而是在后面的某一個(gè)異步時(shí)間點(diǎn)被執(zhí)行蹈丸,這也是Promise機(jī)制實(shí)現(xiàn)的功能。
因此定義Promise時(shí)指定的函數(shù)體是在當(dāng)場(chǎng)就執(zhí)行的呐芥,而定義then()時(shí)指定的函數(shù)體不是當(dāng)場(chǎng)執(zhí)行逻杖,而是在之后以異步的方式執(zhí)行的。
簡(jiǎn)單的說(shuō)
- Promise對(duì)象創(chuàng)建的時(shí)候思瘟,立刻執(zhí)行Promise函數(shù)體荸百,同時(shí)會(huì)標(biāo)記將來(lái)是執(zhí)行resolve還是reject
多說(shuō)一句Promise對(duì)象的最終狀態(tài)只有兩個(gè)要么是resolved,要么是rejected滨攻,所以如果在Promise函數(shù)體里面同時(shí)調(diào)用了resolve和reject(注意Promise函數(shù)體里面不管是調(diào)用了resolve還是reject够话,都不結(jié)束函數(shù),而會(huì)繼續(xù)執(zhí)行后面的代碼)铡买,誰(shuí)先調(diào)用誰(shuí)有效更鲁,后面調(diào)用的無(wú)效,因?yàn)榈谝粋€(gè)調(diào)用就已經(jīng)改變了Promise的最終狀態(tài)奇钞。舉個(gè)例子:
1 function foo(param) {
2 var p = new Promise((resolve, reject) => {
3 resolve("succ");
4 reject("fail");
5 console.log("hello");
6 return "any";
7 console.log("world");
8 });
9 return p
10 }
11
12 foo(1)
13 .then((value) => {
14 console.log("then-1: " + value);
15 })
16 .catch((reason) => {
17 console.log("catch: " + reason);
18 })
運(yùn)行結(jié)果為:
$ node p.js
hello
then-1: succ
- 第3行resolve把promise最終狀態(tài)職位resolved.
- 第4行reject代碼無(wú)效澡为,因?yàn)镻romise的最終狀態(tài)已經(jīng)被置為resolved了,此時(shí)再置成rejected自然無(wú)效景埃。
- 第5行l(wèi)og代碼正常執(zhí)行
- 第6行return返回媒至,這個(gè)值不知道返回到哪里去了。
- 第7行沒(méi)有執(zhí)行谷徙,因?yàn)榈?行已經(jīng)返回了拒啰。
- then/catch負(fù)責(zé)注冊(cè)這些函數(shù)體到對(duì)應(yīng)的resolve/reject函數(shù)鏈上,而不會(huì)馬上就執(zhí)行他們完慧,只是注冊(cè)谋旦。
- 對(duì)他們的執(zhí)行是在稍后以異步事件的方式回調(diào)的;具體的回調(diào)時(shí)間是不確定的。
后面還有Promise.all()等沒(méi)有研究册着,主要是暫時(shí)沒(méi)有用到拴孤;以后有機(jī)會(huì)再整理。