Promise

含義

所謂Promise,簡(jiǎn)單來(lái)說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事情(通常是一個(gè)異步操作)的結(jié)果.從語(yǔ)法上來(lái)說(shuō),Promise是一個(gè)對(duì)象,從它可以獲取異步操作的消息.
Promise有以下兩個(gè)特點(diǎn):

  1. 對(duì)象的狀態(tài)不受外界影響.Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):pending(進(jìn)行中),fulfilled(已成功)rejectd(已失敗).只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài)
  2. 一旦狀態(tài)改變,就不會(huì)再變(resolved已定型),任何時(shí)候都可以得到這個(gè)結(jié)果.如果改變已經(jīng)發(fā)生了,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果

缺點(diǎn):無(wú)法取消Promise,一旦新建就會(huì)立即執(zhí)行;如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反映到外部;當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知進(jìn)展到哪一個(gè)階段

基本用法

Promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來(lái)生成Promise實(shí)例

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolvereject.他們是兩個(gè)函數(shù),由JavaScript引擎提供,不用自己部署.
Promise實(shí)例生成之后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)

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

這兩個(gè)函數(shù)都接受Promise對(duì)象傳出的值作為參數(shù).

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

立即resolved的Promise是在本輪事件循環(huán)的末尾執(zhí)行,總是晚于本輪循環(huán)的同步任務(wù)
一般來(lái)說(shuō),調(diào)用resolvereject以后,Promise的使命就完成了,后面操作應(yīng)該放到then方法里面.所以在它們前面加上return語(yǔ)句,這樣就不會(huì)發(fā)生意外.

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的語(yǔ)句不會(huì)執(zhí)行
  console.log(2);
})

Promise.prototype.then

作用是為Promise實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù).返回的是一個(gè)新的Promise實(shí)例.因此可以采用鏈?zhǔn)綄?xiě)法,即then方法后面再調(diào)用另一個(gè)then方法
采用鏈?zhǔn)降?code>then,可以指定一組按照次序調(diào)用的回調(diào)函數(shù).這時(shí),前一個(gè)回調(diào)函數(shù)有可能返回的還是一個(gè)Promise對(duì)象(有異步操作),這時(shí)后一個(gè)回調(diào)函數(shù)就會(huì)等待該Promise對(duì)象的狀態(tài)發(fā)生變化,才會(huì)被調(diào)用

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

Promise.prototype.catch

Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(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));

如果Promise狀態(tài)已經(jīng)變成resolved,再拋出錯(cuò)誤是無(wú)效的
Promise對(duì)象的錯(cuò)誤具有"冒泡"性質(zhì),會(huì)一直向后傳遞.
一般來(lái)說(shuō),不要在then方法里面定義Reject狀態(tài)的回調(diào)函數(shù)(即then的第二個(gè)參數(shù)),總是使用catch方法.理由是第二種寫(xiě)法可以捕獲前面then方法執(zhí)行中的錯(cuò)誤,也更接近同步的寫(xiě)法(try/catch).
Promise的錯(cuò)誤不會(huì)影響到Promise外部的代碼,天通俗的說(shuō)法是"Promise會(huì)吃掉錯(cuò)誤"
一般總是建議Promise對(duì)象后面要跟catch方法,這樣可以處理Promise內(nèi)部發(fā)生的錯(cuò)誤.catch方法返回的還是一個(gè)Promise對(duì)象,因此后面還可以接著調(diào)用then方法
catch方法之中還可以?huà)伋鲥e(cuò)誤,而且可以繼續(xù)使用catch方法捕獲

Promise.prototype.finally

finally方法用于指定不管Promise對(duì)象狀態(tài)如果,都會(huì)執(zhí)行的操作.該方法是ES7引入標(biāo)準(zhǔn)的
finally方法的回調(diào)函數(shù)不接受任何參數(shù),finally方法里面的操作,應(yīng)該是與狀態(tài)無(wú)關(guān)的,不依賴(lài)與Promise的執(zhí)行結(jié)果
實(shí)現(xiàn)finally代碼:

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

Promise.all

用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例

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

Promise.all方法接受一個(gè)數(shù)組作為參數(shù),p1,p2,p3都是Promise實(shí)例.如果不是則調(diào)用Promise.resolve方法將參數(shù)轉(zhuǎn)為Promise實(shí)例,再進(jìn)一步處理.(Promise.all方法的參數(shù)可以不是數(shù)組,但必須具有Iterator接口,且返回的每個(gè)成員都是Promise實(shí)例)
p的狀態(tài)由p1,p2,p3決定:

  1. 如果三者狀態(tài)都變成fulfilled,p的狀態(tài)才會(huì)變成fulfilled,此時(shí)三者的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)
  2. 只有三者中有一個(gè)被reject,p的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值,會(huì)傳遞給p的回調(diào)函數(shù)

注意,如果作為參數(shù)的Promise實(shí)例,自己定義了catch方法,那么它一旦被rejected,并不會(huì)觸發(fā)Promise.allcatch方法

Promise.race

該方法同樣是將多個(gè)Promise實(shí)例包裝成一個(gè)新的Promise實(shí)例.

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

只要參數(shù)中三個(gè)Promise實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變.那個(gè)率先改變的Promise實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)

Promise.resolve

該方法可以將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象

Promise.resolve('foo')
// 等價(jià)于
new Promise(resolve => resolve('foo'))

Promise.resolve方法的參數(shù)分成四種情況:

  1. 參數(shù)是一個(gè)Promise實(shí)例
  2. 參數(shù)是一個(gè)thenable對(duì)象
    thenable對(duì)象指得是具有then方法的對(duì)象.會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后就立即執(zhí)行thenable對(duì)象的then方法
  3. 參數(shù)不是具有then方法的對(duì)象,或根本不是對(duì)象
    返回一個(gè)新的Promise對(duì)象,狀態(tài)為resolved.所以回調(diào)函數(shù)會(huì)立即執(zhí)行
  4. 不帶有任何參數(shù)
    Promise.resolve方法允許調(diào)用時(shí)不帶任何參數(shù),直接返回一個(gè)resolved狀態(tài)的Promise對(duì)象

需要注意的是,立即resolve的Promise對(duì)象,是在本輪"事件循環(huán)"的結(jié)束時(shí),而不是在下一輪的開(kāi)始時(shí)

Promise.reject

該方法也會(huì)返回一個(gè)新的Promise實(shí)例,該實(shí)例的狀態(tài)為rejected
注意:Promise.reject方法的參數(shù),會(huì)原封不動(dòng)的座位reject的理由,變成后續(xù)方法的參數(shù)

應(yīng)用

  • 加載圖片
const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
  • Generator函數(shù)和Promise的結(jié)合
    使用Generator函數(shù)管理流程,遇到異步操作的時(shí)候,通常返回一個(gè)Promise對(duì)象
function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e);
  }
};

function run (generator) {
  const it = generator();

  function go(result) {
    if (result.done) return result.value;

    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }

  go(it.next());
}

run(g);

Promise.try

實(shí)際開(kāi)發(fā)中,經(jīng)常會(huì)遇到一種情況:不知道或者不想?yún)^(qū)分,一個(gè)函數(shù)是同步函數(shù)還是異步操作,但是享用Promise來(lái)處理

  1. 使用async函數(shù)
const f = () => console.log('now');
(async () => f())();
console.log('next');
// now
// next
  1. 使用new Promise
const f = () => console.log('now');
(
  () => new Promise(
    resolve => resolve(f())
  )
)();
console.log('next');
// now
// next

鑒于該需求很常見(jiàn),所以現(xiàn)在提案,提供Promise.try方法替代上面的寫(xiě)法

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

由于Promise.try為所有操作提供了統(tǒng)一的處理機(jī)制,所以如果想用then方法管理流程,最好都用Promise.try包裝一下.這樣很多好處,其中一點(diǎn)就是處理異常

Promise.try(database.users.get({id: userId}))
  .then(...)
  .catch(...)

事實(shí)上,Promise.try就是模擬try代碼塊,就像Promise.catchmoni的是catch代碼塊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市睁搭,隨后出現(xiàn)的幾起案子赶诊,更是在濱河造成了極大的恐慌,老刑警劉巖园骆,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舔痪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锌唾,警方通過(guò)查閱死者的電腦和手機(jī)锄码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晌涕,“玉大人滋捶,你說(shuō)我怎么就攤上這事∮嗬瑁” “怎么了重窟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)惧财。 經(jīng)常有香客問(wèn)我巡扇,道長(zhǎng)炒考,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任霎迫,我火速辦了婚禮斋枢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘知给。我一直安慰自己瓤帚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布涩赢。 她就那樣靜靜地躺著戈次,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筒扒。 梳的紋絲不亂的頭發(fā)上怯邪,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音花墩,去河邊找鬼悬秉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冰蘑,可吹牛的內(nèi)容都是我干的和泌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼祠肥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼武氓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起仇箱,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤县恕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后剂桥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體忠烛,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年渊额,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了况木。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旬迹,死狀恐怖火惊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奔垦,我是刑警寧澤屹耐,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響惶岭,放射性物質(zhì)發(fā)生泄漏寿弱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一按灶、第九天 我趴在偏房一處隱蔽的房頂上張望症革。 院中可真熱鬧,春花似錦鸯旁、人聲如沸噪矛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)艇挨。三九已至,卻和暖如春韭赘,著一層夾襖步出監(jiān)牢的瞬間缩滨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工泉瞻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脉漏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓瓦灶,卻偏偏與公主長(zhǎng)得像鸠删,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贼陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,706評(píng)論 1 56
  • Promise的含義: ??Promise是異步編程的一種解決方案巧娱,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評(píng)論 0 16
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,494評(píng)論 0 8
  • 00禁添、前言Promise 是異步編程的一種解決方案撮胧,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)...
    夜幕小草閱讀 2,133評(píng)論 0 12
  • 爸爸媽媽第一次登泰山的時(shí)候是在2006年10月2號(hào)老翘,那個(gè)時(shí)候我們正上大二芹啥。學(xué)生時(shí)代比較貧困的我們,一人花了10塊錢(qián)...
    辛馨閱讀 338評(píng)論 0 0