Promise (Bluebird) 筆記整理

1. 基本用法

**例 1.1 創(chuàng)造一個Promise實例 **

var promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

例 1.2 用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then的第二個參數(shù)一般不寫,最后統(tǒng)一用catch捕捉異常

例 1.3 Promise新建后就會立即執(zhí)行

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});
promise.then(function() {
  console.log('Resolved.');
});
console.log('Hi!');

// Promise
// Hi!
// Resolved

Promise新建后立即執(zhí)行浙宜,所以首先輸出的是“Promise”。然后,then方法指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會執(zhí)行影兽,所以“Resolved”最后輸出鲫忍。

例 1.4 resolve()和reject()帶有參數(shù)
resolve(xxx)reject(new Error()),則參數(shù)會被傳遞給回調(diào)函數(shù)。reject參數(shù)通常是new Error()表示拋出的錯誤; resolve函數(shù)的參數(shù)除了正常的值以外闸衫,還可能是另一個Promise實例 ,表示異步操作的結(jié)果有可能是一個值诽嘉,也有可能是另一個異步操作.

var p1 = new Promise((resolve, reject) => {
    resolve('111')
    // reject(new Error('222'))
})
p1
.then(data => console.log(data))  // '111'
.catch(err => console.log(err)) // 捕獲reject錯誤
var p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

var p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail

p1是一個Promise蔚出,3秒之后變?yōu)?code>rejected。p2的狀態(tài)在1秒之后改變虫腋,resolve方法返回的是p1骄酗。由于p2返回的是另一個 Promise,導(dǎo)致p2自己的狀態(tài)無效了悦冀,由p1的狀態(tài)決定p2的狀態(tài)趋翻。所以,后面的then語句都變成針對后者(p1)盒蟆。又過了2秒踏烙,p1變?yōu)?code>rejected,導(dǎo)致觸發(fā)catch方法指定的回調(diào)函數(shù)茁影。

例 1.5 Promise.prototype.then()
then方法的第一個參數(shù)是Resolved狀態(tài)的回調(diào)函數(shù)宙帝,第二個參數(shù)(可選)是Rejected狀態(tài)的回調(diào)函數(shù)。
采用鏈?zhǔn)降?code>then募闲,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)步脓。這時,前一個回調(diào)函數(shù)浩螺,有可能返回的還是一個Promise對象(即有異步操作)靴患,這時后一個回調(diào)函數(shù),就會等待該P(yáng)romise對象的狀態(tài)發(fā)生變化要出,才會被調(diào)用鸳君。

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("Resolved: ", comments),
  err => console.log("Rejected: ", err)
);

例 1.6 Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)患蹂。

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));
var promise = new Promise(function(resolve, reject) {
  // resolve('ok')
  // 寫法 1 
  reject(new Error('test'));
  // 寫法 2
  throw new Error('test');
  // 寫法 3
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});

promise.catch(function(error) {
  console.log(error);
});

注意:若以上代碼resolve('ok')生效或颊,即Promise 在resolve語句后面砸紊,再拋出錯誤,不會被捕獲囱挑,等于沒有拋出醉顽。因為 Promise 的狀態(tài)一旦改變,就永久保持該狀態(tài)平挑,不會再變了游添。

var promise = new Promise(function(resolve, reject) {
  resolve('ok');
  setTimeout(function() { throw new Error('test') }, 0)
});
promise.then(function(value) { console.log(value) });
// ok
// Uncaught Error: test

上面代碼中,Promise 指定在下一輪“事件循環(huán)”再拋出錯誤通熄,結(jié)果由于沒有指定使用try...catch語句唆涝,就冒泡到最外層,成了未捕獲的錯誤唇辨。因為此時廊酣,Promise的函數(shù)體已經(jīng)運(yùn)行結(jié)束了,所以這個錯誤是在Promise函數(shù)體外拋出的赏枚。

Node 有一個unhandledRejection事件啰扛,專門監(jiān)聽未捕獲的reject錯誤.

process.on('unhandledRejection', function (err, p) {
  console.error(err.stack)
});

跟傳統(tǒng)的try/catch代碼塊不同的是,如果沒有使用catch方法指定錯誤處理的回調(diào)函數(shù)嗡贺,Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應(yīng)鞍帝。

var someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行會報錯诫睬,因為x沒有聲明
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  console.log('everything is great');
});

上面代碼中,someAsyncThing函數(shù)產(chǎn)生的Promise對象會報錯帕涌,但是由于沒有指定catch方法摄凡,這個錯誤不會被捕獲,也不會傳遞到外層代碼蚓曼,導(dǎo)致運(yùn)行后沒有任何輸出亲澡。注意,Chrome瀏覽器不遵守這條規(guī)定纫版,它會拋出錯誤“ReferenceError: x is not defined”床绪。

因為Promise內(nèi)部的錯誤不會冒泡到全局,不管以then方法或catch方法結(jié)尾其弊,要是最后一個方法拋出錯誤癞己,都有可能無法捕捉到,此時你需要done()--例1.11

例 1.7 Promise.all
Promise.all方法用于將多個Promise實例 ,包裝成一個新的Promise實例 梭伐。

var p = Promise.all([p1, p2, p3]);

Promise.all方法接受一個數(shù)組作為參數(shù)痹雅,p1p2糊识、p3都是Promise對象的實例 绩社,如果不是摔蓝,就會先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise實例 愉耙,再進(jìn)一步處理贮尉。(Promise.all方法的參數(shù)可以不是數(shù)組,但必須具有Iterator接口劲阎,且返回的每個成員都是Promise實例 绘盟。)

p的狀態(tài)由p1、p2悯仙、p3決定龄毡,分成兩種情況:

  1. 只有p1、p2锡垄、p3的狀態(tài)都變成fulfilled沦零,p的狀態(tài)才會變成fulfilled,此時p1货岭、p2路操、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)千贯。
  • 只要p1屯仗、p2、p3之中有一個被rejected搔谴,p的狀態(tài)就變成rejected魁袜,此時第一個被reject的實例 的返回值,會傳遞給p的回調(diào)函數(shù)敦第。

forEach() 處理promise
即在使用for循環(huán)峰弹、foreach()的時候如何使用promise

// 錯誤示范
// I want to remove() all docs
db.allDocs({include_docs: true}).then(function (result) {
    result.rows.forEach(function (row) {
        db.remove(row.doc);
    });
}).then(function () {
    // I naively believe all docs have been removed() now!
});

這段代碼的問題在于第一個回調(diào)函數(shù)實際上返回的是 undefined,也就意味著第二個函數(shù)并不是在所有的db.remove() 執(zhí)行結(jié)束之后才執(zhí)行芜果。事實上鞠呈,第二個函數(shù)的執(zhí)行不會有任何延時,它執(zhí)行的時候被刪除的doc數(shù)量可能為任意整數(shù)右钾。

這時候你需要的是 Promise.all()

// 正確的寫法
db.allDocs({include_docs: true}).then(function (result) {
    return Promise.all(result.rows.map(function (row) {
        return db.remove(row.doc);
    }));
}).then(function (arrayObject) {
    // All docs have really been removed() now!
})

例 1.8 Promise.race()

var p = Promise.race([p1, p2, p3]);

只要p1蚁吝、p2p3之中有一個實例 率先改變狀態(tài)霹粥,p的狀態(tài)就跟著改變灭将。那個率先改變的 Promise 實例 的返回值,就傳遞給p的回調(diào)函數(shù)后控。

如果指定時間內(nèi)沒有獲得結(jié)果庙曙,就將Promise的狀態(tài)變?yōu)?code>reject,否則變?yōu)?code>resolve浩淘。

例 1.9 Promise.resolve()

**參數(shù)是一個Promise實例 **
如果參數(shù)是Promise實例 捌朴,那么Promise.resolve將不做任何修改吴攒、原封不動地返回這個實例 。

參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve方法會將這個對象轉(zhuǎn)為Promise對象砂蔽,然后就立即執(zhí)行thenable對象的then方法洼怔。

參數(shù)不是具有then方法的對象,或根本就不是對象
如果參數(shù)是一個原始值左驾,或者是一個不具有then方法的對象镣隶,則Promise.resolve方法返回一個新的Promise對象,狀態(tài)為Resolved诡右。

var p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

不帶有任何參數(shù)
Promise.resolve()直接返回一個Resolve狀態(tài)的Promise對象

var p = Promise.resolve();
p.then(function () {
  // ...
});

注意安岂,立即resolve的Promise對象,是在本輪“事件循環(huán)”(event loop)的結(jié)束時帆吻,而不是在下一輪“事件循環(huán)”的開始時域那。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

記住,有可能拋出錯誤的代碼都有可能因為錯誤被吞噬而對你的工作造成困擾猜煮。但是如果你用 Promise.resolve() 包裝了代碼的話次员,你永遠(yuǎn)都可以在代碼后面加上 catch()

例 1.10 Promise.reject()
Promise.reject(reason)方法也會返回一個新的 Promise 實例 王带,該實例 的狀態(tài)為rejected淑蔚。

注意Promise.reject()方法的參數(shù)愕撰,會原封不動地作為reject的理由束倍,變成后續(xù)方法的參數(shù)。這一點與Promise.resolve方法不一致盟戏。

const thenable = {
  then(resolve, reject) {
    reject('出錯了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true
// e參數(shù)是thenable對象

例 1.11 done()
Promise對象的回調(diào)鏈,不管以then方法或catch方法結(jié)尾甥桂,要是最后一個方法拋出錯誤柿究,都有可能無法捕捉到(因為Promise內(nèi)部的錯誤不會冒泡到全局)。因此黄选,我們可以提供一個done方法蝇摸,總是處于回調(diào)鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤办陷。

asyncFunc()
  .then(f1)
  .catch(r1)
  .then(f2)
  .done();

實現(xiàn)方法

Promise.prototype.done = function(onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 拋出一個全局錯誤
      setTimeout(() => { throw reason }, 0);
      });
}

done方法的使用貌夕,可以像then方法那樣用,提供FulfilledRejected狀態(tài)的回調(diào)函數(shù)民镜,也可以不提供任何參數(shù)啡专。但不管怎樣,done都會捕捉到任何可能出現(xiàn)的錯誤制圈,并向全局拋出们童,然后用unhandledRejection捕獲畔况。

例 1.12 finally
finally方法用于指定不管Promise對象最后狀態(tài)如何,都會執(zhí)行的操作慧库。它與done方法的最大區(qū)別跷跪,它接受一個普通的回調(diào)函數(shù)作為參數(shù),該函數(shù)不管怎樣都必須執(zhí)行齐板。

下面是一個例子吵瞻,服務(wù)器使用Promise處理請求,然后使用finally方法關(guān)掉服務(wù)器甘磨。

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);

實現(xiàn)方法

Promise.prototype.finally = function (cb) {
  let P = this.constructor;   // 返回對此對象 Promise 的引用
  return this.then(
    value => P.resolve(cb()).then(() => value),
    reason => P.resolve(cb()).then(() => { throw reason })
    )
}

上面代碼中橡羞,不管前面的Promise是fulfilled還是rejected,都會執(zhí)行回調(diào)函數(shù)cb宽档。

例 1.13 Promise.try()
實際開發(fā)中尉姨,經(jīng)常遇到一種情況:不知道或者不想?yún)^(qū)分,函數(shù)f是同步函數(shù)還是異步操作吗冤,但是想用 Promise 來處理它又厉。因為這樣就可以不管f是否包含異步操作,都用then方法指定下一步流程椎瘟,用catch方法處理f拋出的錯誤覆致。

使用Promise.try()

const f = () => console.log('111');
Promise.try(f);
console.log('222');
// 111
// 222

由于Promise.try為所有操作提供了統(tǒng)一的處理機(jī)制,所以如果想用then方法管理流程肺蔚,最好都用Promise.try包裝一下煌妈。這樣有許多好處,其中一點就是可以更好地管理異常宣羊。

function getUsername(userId) {
  return database.users.get({id: userId})
  .then(function(user) {
    return user.name;
  });
}

database.users.get()可能會拋出同步錯誤或者異步錯誤璧诵。

// 拋出異步錯誤
database.users.get({id: userId})
.then(...)
.catch(...)

// 同步錯誤(比如數(shù)據(jù)庫連接錯誤,具體要看實現(xiàn)方法)
try {
  database.users.get({id: userId})
  .then(...)
  .catch(...)
} catch (e) {
  // ...
}

// 更優(yōu)雅的操作仇冯,相信你會喜歡的
Promise.try(database.users.get({id: userId}))
  .then(...)
  .catch(...)

事實上之宿,Promise.try就是模擬try代碼塊,就像promise.catch模擬的是catch代碼塊苛坚。

2. 更多

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末比被,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泼舱,更是在濱河造成了極大的恐慌等缀,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娇昙,死亡現(xiàn)場離奇詭異尺迂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門枪狂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來危喉,“玉大人,你說我怎么就攤上這事州疾」枷蓿” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵严蓖,是天一觀的道長薄嫡。 經(jīng)常有香客問我,道長颗胡,這世上最難降的妖魔是什么毫深? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮毒姨,結(jié)果婚禮上哑蔫,老公的妹妹穿的比我還像新娘。我一直安慰自己弧呐,他們只是感情好闸迷,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俘枫,像睡著了一般腥沽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸠蚪,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天今阳,我揣著相機(jī)與錄音,去河邊找鬼茅信。 笑死盾舌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蘸鲸。 我是一名探鬼主播矿筝,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棚贾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起榆综,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤妙痹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鼻疮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怯伊,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年判沟,在試婚紗的時候發(fā)現(xiàn)自己被綠了耿芹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崭篡。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吧秕,靈堂內(nèi)的尸體忽然破棺而出琉闪,到底是詐尸還是另有隱情,我是刑警寧澤砸彬,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布颠毙,位于F島的核電站,受9級特大地震影響砂碉,放射性物質(zhì)發(fā)生泄漏蛀蜜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一增蹭、第九天 我趴在偏房一處隱蔽的房頂上張望滴某。 院中可真熱鬧,春花似錦滋迈、人聲如沸霎奢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椰憋。三九已至,卻和暖如春赔退,著一層夾襖步出監(jiān)牢的瞬間橙依,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工硕旗, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留窗骑,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓漆枚,卻偏偏與公主長得像创译,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子墙基,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

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

  • Promiese 簡單說就是一個容器软族,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,語法上說残制,Pr...
    雨飛飛雨閱讀 3,352評論 0 19
  • 00初茶、前言Promise 是異步編程的一種解決方案颗祝,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)...
    夜幕小草閱讀 2,129評論 0 12
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,167評論 0 16
  • //本文內(nèi)容起初摘抄于 阮一峰 作者的譯文螺戳,用于記錄和學(xué)習(xí)搁宾,建議觀者移步于原文 概念: 所謂的Promise,...
    曾經(jīng)過往閱讀 1,231評論 0 7
  • 整個夜里倔幼,她都無法安睡盖腿。今晚的月亮?xí)绾文? 她始終懷念一個人。一個令她一輩子都值得愛的人凤藏。記得那天春游的時候奸忽,他...
    icexu閱讀 274評論 0 0