Promise對象

1秽梅、Promise的含義:是異步編程的一種解決方案。Promise是一個對象植影,從它可以獲取異步操作的消息。Promise提供統(tǒng)一的API涎永,各種異步操作都可以用同樣的方法進行處理

Promise對象有以下兩個特點:

(1)對象的狀態(tài)不受外界影響思币。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)羡微、fulfilled(已成功)和rejected(已失敼榷觥)。只有異步操作的結果妈倔,可以決定當前是哪一種狀態(tài)博投,任何其他操作都無法改變這個狀態(tài)。這也是Promise這個名字的由來盯蝴,它的英語意思就是“承諾”毅哗,表示其他手段無法改變。
(2)一旦狀態(tài)改變捧挺,就不會再變黎做,任何時候都可以得到這個結果。Promise對象的狀態(tài)改變松忍,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected蒸殿。只要這兩種情況發(fā)生,狀態(tài)就凝固,不會再變宏所,會一直保持這個結果酥艳,這是稱為resolves(已定型)。如果改變已經(jīng)發(fā)生爬骤,再對Promise對象添加回調(diào)函數(shù)充石,也會得到這個結果

2、基本用法(ES6規(guī)定霞玄,Promise對象是一個構造函數(shù)骤铃,用來生成Promise實例)

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

Promise構造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject坷剧,它們是兩個函數(shù)惰爬。

resolve函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸膒ending變?yōu)閞esolved)惫企,在異步操作成功是調(diào)用撕瞧,并將異步操作的結果,作為參數(shù)傳遞出去狞尔;reject函數(shù)的作用是丛版,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸膒ending變?yōu)閞ejected),在異步操作失敗時調(diào)用偏序,并將異步操作報出的錯誤页畦,作為參數(shù)傳遞出去

Promise實例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)

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

then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)研儒。第一個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)閞esolved時調(diào)用寇漫,第二個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)閞ejected時調(diào)用。其中殉摔,第二個函數(shù)是可選的州胳,不一定要提供。

const p1 = new Promise(function (resolve, reject) {
  setTimeout( () => reject(new Error('fail')), 3000)
})

const 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)閞ejected。p2的狀態(tài)在1秒之后改變碗硬,resolve方法返回的是p1瓤湘。由于p2返回的是另一個Promise,導致p2自己的狀態(tài)無效了恩尾,由于p2的狀態(tài)決定p2的狀態(tài)弛说。所以,后面的then語句都變成針對者(p1)翰意。又過了2秒木人,p1變?yōu)閞ejected信柿,導致觸發(fā)catch方法指定的回調(diào)函數(shù)。
注意:調(diào)用resolve或reject并不會終結Promise的參數(shù)函數(shù)的執(zhí)行醒第。

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

上面代碼中渔嚷,調(diào)用resolve(1)以后,后面的console.log(2)還是會執(zhí)行稠曼,并且會首先打印出來形病。這是因為立即resolve的Promise是在本輪事件循環(huán)得到末尾執(zhí)行,總是晚于本輪循環(huán)的同步任務霞幅。
3漠吻、Promise.prototype.then()
Promise實例具有then方法,也就是說司恳,then方法是定義在原型對象Promise.prototype上的途乃。它的作用是為Promise實例添加狀態(tài)改變時的回調(diào)函數(shù)。
then方法返回的是一個新的Promise實例(注意:不是原來那個Promise實例)抵赢。因為可以采用鏈式寫法,即then方法后面再調(diào)用另一個then方法唧取。

getISON('posts.json').then(function(json){
  return json.post
}).then(function(post){
 // ....
}

上面的代碼使用then方法铅鲤,依次指定了兩個回調(diào)函數(shù)。第一個回調(diào)函數(shù)完成以后枫弟,會將返回結果作為參數(shù)邢享,傳入第二個回調(diào)函數(shù)。
4淡诗、Promise.prototype.catch()
Promise.prototype.catch()方法是.then(null, rejection) 或 .then(undefined, rejection)的別名骇塘,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。

getJSON('/posts.json).then(function(posts){
 // ...
}).catch(function(error){
 //處理getJSON和前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
console.log('發(fā)生錯誤韩容!‘款违,error)
})

上面代碼中,getJSON()方法返回一個Promise對象群凶,如果該對象狀態(tài)變?yōu)閞esolved插爹,則會調(diào)用then()方法指定的回調(diào)函數(shù);如果異步操作拋出錯誤请梢,狀態(tài)就會變?yōu)閞ejected,就回調(diào)catch()方法指定的回調(diào)函數(shù)赠尾,處理這個錯誤。另外毅弧,then()方法指定的回調(diào)函數(shù)气嫁,如果運行中拋出錯我,也會被catch()方法捕獲够坐。

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

//等同于
 p.then((val) => console.log('fulfilled:', val))
 .catch(null, (err) => console.log('rejected', err))

5寸宵、Promise.prototype.finally()
finally()方法用于指定不管Promise對象最后狀態(tài)如何崖面,都會執(zhí)行的操作。

promise
.then(result => {...})
.catch(error => {...})
.finally(() => {...})

上面代碼中邓馒,不管promise最后的狀態(tài)嘶朱,在執(zhí)行完then或catch指定的回調(diào)函數(shù)以后,都會執(zhí)行finally方法指定的回調(diào)函數(shù)

服務器使用Promise處理請求光酣,然后使用finally方法關掉服務器疏遏。

 server.listen(port)
 .then(function () {
 //...
 }).finally(server.stop)  // 關掉服務器

finally方法的回調(diào)函數(shù)不接受任何參數(shù),這意味著沒有辦法知道救军,前面的Promise狀態(tài)到底是fulfilled還是rejected财异。這表明,finally方法里面的操作唱遭,應該是與狀態(tài)無關得到戳寸,不依賴于Promise的執(zhí)行結果。
6拷泽、Promise.all()
Promise.all()方法用于將多個Promise實例疫鹊,包裝成一個新的Promsie實例。

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

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ù)颅围。
(2)只要p1、p2恨搓、p3之中有一個被rejected,p的狀態(tài)就變成rejected谷浅,此時第一個被rejected的實例的返回值,會傳遞給p的回調(diào)函數(shù)奶卓。

const promise = [2,3,5,7,11,13].map(function(id) {
   return getJSON('/post/' + id + '.json')
})
Promise.all(promise).then(function (posts) {
  // ....
}).catch(function(reason){
 //...
})

7一疯、Promise.race()
Promise.race()方法同樣是將多個Promise實例,包裝成一個新的Promise實例夺姑。

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

上面代碼中墩邀,只要p1、p2盏浙、p3之中有一個實例率先改變狀態(tài)眉睹,p的狀態(tài)就跟著改變荔茬。那個率先改變的Promise實例的返回值,就傳遞給p的回調(diào)函數(shù)竹海。

8慕蔚、Promise.allSettled()
Promise.allSettled()方法接受一組Promise實例作為參數(shù),包裝成一個新的Promise實例斋配。只有等到所有這些參數(shù)實例都返回結果孔飒,不管是fulfilled還是rejected,包裝實例才會結束艰争。

const promises = [
   fetch('/api-1'),
   fetch('/api-2'),
   fetch('/api-3'),
]
await Promise.allSettled(promises)
removeLoadingIndicator()

上面代碼對服務器發(fā)出三個請求坏瞄,等到三個請求都結束,不管請求成功還是失敗甩卓,加載滾動圖標就會消失鸠匀。
該方法返回的新的Promise實例,一旦結束逾柿,狀態(tài)總是fulfilled缀棍,不會變成rejected。狀態(tài)變成fulfilled后机错,Promise的監(jiān)聽函數(shù)接收到的參數(shù)是一個數(shù)組爬范,每個成員對應一個傳入Promise.allSettled()的Promise實例。
9毡熏、Promise.any()
Promise.ang()方法接受一組Promise實例作為參數(shù)坦敌,包裝成一個新的Promise實例侣诵。只要參數(shù)實例有一個變成fulfilled狀態(tài)痢法,包裝實例就會變成fulfilled狀態(tài);如果所有參數(shù)實例都變成rejected狀態(tài)杜顺,包裝實例就會變成rejected狀態(tài)财搁。該方法目前是一個第三階段的提案。

 const promise = [
  fetch('/endpoint-a').then(() => 'a'),
  fetch('/endpoint-b').then(() => 'b'),
  fetch('/endpoint-c').then(() => 'c')
];

try {
  const first = await Promise.any(promise);
  console.log(first)
} catch (error) {
  console.log(error)
}

10躬络、Promise.resolve()
有時需要將現(xiàn)有對象轉為Promise對象尖奔,Promise.resolve()方法就起到這個作用。

const jsPromise = Promise.resolve($.ajax('/whatever.json'))

上面代碼將jQuery生成的deferred對象穷当,轉為一個新的Promise對象提茁。
Promise.resolve()等價于下面的寫法。

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

Promise.resolve方法的參數(shù)分成四種情況馁菜。
(1)參數(shù)是一個Promise實例
如果參數(shù)是Promise實例茴扁,那么Promsie.resolve將不做任何修改,原封不動地返回這個實例汪疮。
(2)參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象峭火,比如下面這個對象

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

Promise.resolve方法會將這個對象轉為Promise對象毁习,然后就立即執(zhí)行thenable對象的then方法。

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

let p1 = Promise.resolve(thenable)
  p1.then(function(value) {
    console.log(value)  // 42
})

(3)參數(shù)不是具有then方法的對象卖丸,或根本就不是對象
如果參數(shù)是一個原始值纺且,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象稍浆,狀態(tài)為resolved载碌。

const p = Promise.resolve('Hello')
p.then(function (s) {
  console.log(s)  // Hello
})

(4)不帶有任何參數(shù)
Promise.resolve()方法允許調(diào)用時不帶參數(shù),直接返回一個resolved狀態(tài)的Promsie對象

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

注意:立即resolve()的Promise對象粹湃,是在本輪“事件循環(huán)”(event loop)的結束時執(zhí)行恐仑,而不是在下一輪“事件循環(huán)”的開始時。

setTimeout(function () {
  console.log('three')
},0)
Promise.resolve().then(function () {
    console.log('two')
})
console.log('one')
 // one 
 // two
 // three

上面代碼中为鳄,setTimeout(fn,0)在下一輪“事件循環(huán)‘開始時執(zhí)行裳仆。Promise.resolve()在本輪“事件循環(huán)”結束時執(zhí)行,console.log('one')則是立即執(zhí)行
11孤钦、Promise.reject()
Promise.reject(reason)方法也會返回一個新的Promise實例歧斟,該實例的狀態(tài)為rejected。

const p = Promise.reject('出錯了')
 // 等同于
const p = new Promise(resolve, reject) => rejecct('出錯了'))
p.then(null, function (s) {
 console.log(s)
})
//出錯了

原文鏈接:https://es6.ruanyifeng.com/#docs/promise

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偏形,一起剝皮案震驚了整個濱河市静袖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俊扭,老刑警劉巖队橙,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萨惑,居然都是意外死亡捐康,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門庸蔼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來解总,“玉大人,你說我怎么就攤上這事姐仅』ǚ悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵掏膏,是天一觀的道長劳翰。 經(jīng)常有香客問我,道長馒疹,這世上最難降的妖魔是什么佳簸? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮行冰,結果婚禮上溺蕉,老公的妹妹穿的比我還像新娘伶丐。我一直安慰自己,他們只是感情好疯特,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布哗魂。 她就那樣靜靜地躺著,像睡著了一般漓雅。 火紅的嫁衣襯著肌膚如雪录别。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天邻吞,我揣著相機與錄音组题,去河邊找鬼。 笑死抱冷,一個胖子當著我的面吹牛崔列,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旺遮,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赵讯,長吁一口氣:“原來是場噩夢啊……” “哼吭敢!你這毒婦竟也來了老厌?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竖瘾,失蹤者是張志新(化名)和其女友劉穎鸣剪,沒想到半個月后组底,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡筐骇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年债鸡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拥褂。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡娘锁,死狀恐怖牙寞,靈堂內(nèi)的尸體忽然破棺而出饺鹃,到底是詐尸還是另有隱情,我是刑警寧澤间雀,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布悔详,位于F島的核電站,受9級特大地震影響惹挟,放射性物質(zhì)發(fā)生泄漏茄螃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一连锯、第九天 我趴在偏房一處隱蔽的房頂上張望归苍。 院中可真熱鬧用狱,春花似錦、人聲如沸拼弃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吻氧。三九已至溺忧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盯孙,已是汗流浹背鲁森。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留振惰,地道東北人歌溉。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像骑晶,于是被迫代替她去往敵國和親研底。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354