Promise物件
狀態
Promise物件有三種狀態:
pending
: 進行中fulfilled
: 已成功rejected
: 已失敗
resolve
函式:會由 pending-> fulfilled
, 狀態凝固不會再次改變
reject
函式:會由 pending-> rejected
, 狀態凝固不會再次改變
建立一個Promise例項
Promise建構函式接受一個函式作為引數,該函式的兩個引數分別是resolve
和reject
。它們是兩個函式,由 JavaScript 引擎提供。
function getNumber(){ const promise = new Promise((resolve, reject) => { //做一些非同步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //ceil對num進行上舍入 生成1-10的隨機數 if(num<=5){ return resolve(num); } else{ return reject('數字太大了'); } }, 2000); }); return promise; }
呼叫resolve
或reject
並不會終結 Promise 的引數函式的執行。如果不想執行後續程式碼,我們可以在resolve
或reject
前面加上return
語句。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
上面程式碼中,呼叫resolve(1)
以後,後面的console.log(2)
還是會執行,並且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件迴圈的末尾執行,總是晚於本輪迴圈的同步任務。
Promise常用方法
Promise.then()
then
方法可以接受兩個引數,第一個對應fulfilled
狀態的回撥函式,第二個對應rejected
狀態的回撥函式。第二個引數是可選的,不建議使用,最好使用catch
方法
then
、catch()
方法返回的是一個Promise例項,存在以下三種情況
- 當返回非Promise物件的值時,
then
和catch
都生成一個狀態為fulfilled
的Promise物件,並把該返回值傳入Promise鏈的下一環節 - 當返回值為Promise物件時,生成的Promise物件的狀態由被返回的Promise物件決定,傳入Promise鏈下一環節的值也由這個被返回的Promise決定
- 當Promise的回撥函式中丟擲錯誤時,
then
和catch
都生成一個狀態為rejected
的Promise物件,並把丟擲的錯誤物件傳入Promise鏈的下一環節。
getNumber().then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason){
console.log('rejected');
console.log(reason);
}
);
Promise.catch()
catch()
方法效果和寫在then的第二個引數裡面一樣,對應rejected
狀態的回撥函式。不過它還有另外一個作用:在執行fulfilled
狀態的回撥函式(也就是上面then
中的第一個引數)時,如果丟擲異常了,那麼並不會報錯卡死js,而是會進到catch
方法中。
catch()
方法返回的還是一個 Promise
例項,因此後面還可以接著呼叫then()
方法。
getNumber().then(function(data){
console.log('resolved');
console.log(data);
console.log(somedata); //此處的somedata未定義 ReferenceError: somedata is not defined
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
Promise.finally()
不管promise
最後的狀態,在執行完then
或catch
指定的回撥函式以後,都會執行finally
方法指定的回撥函式。
finally
方法的回撥函式不接受任何引數。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
Promise.all()
Promise.all()
方法用於將多個 Promise 例項,包裝成一個新的 Promise 例項。
const p = Promise.all([p1, p2, p3]);
上面程式碼中,Promise.all()
方法接受一個數組作為引數,p1
、p2
、p3
都是 Promise 例項,如果不是,就會先呼叫Promise.resolve
方法,將引數轉為 Promise 例項,再進一步處理。另外,Promise.all()
方法的引數可以不是陣列,但必須具有 Iterator 介面,且返回的每個成員都是 Promise 例項。
p
的狀態由p1
、p2
、p3
決定,分成兩種情況。
-
只有
p1
、p2
、p3
的狀態都變成fulfilled
,p
的狀態才會變成fulfilled
,此時p1
、p2
、p3
的返回值組成一個數組,傳遞給p
的回撥函式。 -
只要
p1
、p2
、p3
之中有一個被rejected
,p
的狀態就變成rejected
,此時第一個被reject
的例項的返回值,會傳遞給p
的回撥函式。
Promise.resolve()
Promise.resolve()
方法會將現有物件轉為 Promise 物件,該例項的狀態為fulfilled
。
Promise.resolve('burc')
// 等同於
new Promise(resolve => resolve('burc'))
需要注意的是,立即resolve()
的 Promise 物件,是在本輪“事件迴圈”(event loop)的結束時執行,而不是在下一輪“事件迴圈”的開始時。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve('burc').then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
上面程式碼中,setTimeout(fn, 0)
在下一輪“事件迴圈”開始時執行,Promise.resolve()
在本輪“事件迴圈”結束時執行,console.log('one')
則是立即執行,因此最先輸出。
Promise.reject()
Promise.reject(reason)
方法也會返回一個新的 Promise例項,該例項的狀態為rejected
。
const p = Promise.reject('出錯了');
// 等同於
const p = new Promise((resolve, reject) => reject('出錯了'))
Promise.reject()
方法的引數,會原封不動地作為reject
的理由,變成後續方法的引數。
Promise.reject('出錯了').catch(e => {
console.log(e === '出錯了')
})
// true
鏈式呼叫
Promise 是非同步程式設計的一種解決方案,它的鏈式呼叫成功解決了傳統的回撥地獄難題,but,公司裡的老程式碼用promise也寫出了回撥地獄,這很沒有精神,下面是程式碼示例,論如何正確使用promise
promise錯誤用法 VS promise正確用法