node.js 如何使用Promise

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):

  1. 定義Promise時(shí)需要一個(gè)參數(shù)募寨,這個(gè)參數(shù)是一個(gè)函數(shù),我把它叫做Promise函數(shù)體
function (resolve, reject) { ... }
  1. Promise函數(shù)體接受兩個(gè)參數(shù)
// 參數(shù)resolve和reject也是一個(gè)函數(shù)芝发,原型定義如下:
// function resolve(value) {...}
// function reject(reason) {...}
  1. 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的原型是一樣的访敌,這就明白了吧凉敲。

  1. promise.then()函數(shù)的返回值是一個(gè)Promise對(duì)象,這是為了構(gòu)造then()函數(shù)鏈。
    promise.catch()函數(shù)的返回值也是一個(gè)Promise對(duì)象(盡管我們通常不會(huì)使用這個(gè)對(duì)象)爷抓。
  2. then()函數(shù)參數(shù)函數(shù)的返回值势决,是作為下一個(gè)then函數(shù)鏈的輸入?yún)?shù)。
    如果返回值是一個(gè)Promise對(duì)象除外蓝撇,后面有分析果复。
  3. 這里要弄清楚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. 辦法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
  1. 辦法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

所以

  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ù)體。
  2. 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ō)

  1. 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)返回了拒啰。
  1. then/catch負(fù)責(zé)注冊(cè)這些函數(shù)體到對(duì)應(yīng)的resolve/reject函數(shù)鏈上,而不會(huì)馬上就執(zhí)行他們完慧,只是注冊(cè)谋旦。
  2. 對(duì)他們的執(zhí)行是在稍后以異步事件的方式回調(diào)的;具體的回調(diào)時(shí)間是不確定的。

后面還有Promise.all()等沒(méi)有研究册着,主要是暫時(shí)沒(méi)有用到拴孤;以后有機(jī)會(huì)再整理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甲捏,一起剝皮案震驚了整個(gè)濱河市演熟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌司顿,老刑警劉巖芒粹,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異大溜,居然都是意外死亡化漆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)猎提,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)获三,“玉大人旁蔼,你說(shuō)我怎么就攤上這事锨苏。” “怎么了棺聊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵伞租,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我限佩,道長(zhǎng)葵诈,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任祟同,我火速辦了婚禮作喘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晕城。我一直安慰自己泞坦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布砖顷。 她就那樣靜靜地躺著贰锁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滤蝠。 梳的紋絲不亂的頭發(fā)上豌熄,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音物咳,去河邊找鬼锣险。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芯肤。 我是一名探鬼主播夯接,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纷妆!你這毒婦竟也來(lái)了盔几?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掩幢,失蹤者是張志新(化名)和其女友劉穎逊拍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體际邻,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芯丧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了世曾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缨恒。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖轮听,靈堂內(nèi)的尸體忽然破棺而出骗露,到底是詐尸還是另有隱情,我是刑警寧澤血巍,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布萧锉,位于F島的核電站,受9級(jí)特大地震影響述寡,放射性物質(zhì)發(fā)生泄漏柿隙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一鲫凶、第九天 我趴在偏房一處隱蔽的房頂上張望禀崖。 院中可真熱鬧,春花似錦螟炫、人聲如沸波附。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叶雹。三九已至,卻和暖如春换吧,著一層夾襖步出監(jiān)牢的瞬間折晦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工沾瓦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留满着,地道東北人谦炒。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像风喇,于是被迫代替她去往敵國(guó)和親宁改。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文適用的讀者 本文寫(xiě)給有一定Promise使用經(jīng)驗(yàn)的人魂莫,如果你還沒(méi)有使用過(guò)Promise还蹲,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,299評(píng)論 6 19
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持耙考,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券谜喊,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 11,025評(píng)論 26 95
  • 你不知道JS:異步 第三章:Promises 在第二章倦始,我們指出了采用回調(diào)來(lái)表達(dá)異步和管理并發(fā)時(shí)的兩種主要不足:缺...
    purple_force閱讀 2,058評(píng)論 0 4
  • 弄懂js異步 講異步之前斗遏,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,706評(píng)論 0 5
  • Promise的含義: ??Promise是異步編程的一種解決方案鞋邑,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,167評(píng)論 0 16