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)
})
//出錯了