1. 程式人生 > 實用技巧 >Promise物件

Promise物件

狀態

Promise物件有三種狀態:

  • pending: 進行中
  • fulfilled: 已成功
  • rejected: 已失敗

resolve 函式:會由 pending-> fulfilled, 狀態凝固不會再次改變

reject 函式:會由 pending-> rejected, 狀態凝固不會再次改變

建立一個Promise例項

Promise建構函式接受一個函式作為引數,該函式的兩個引數分別是resolvereject。它們是兩個函式,由 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;            
}

呼叫resolvereject並不會終結 Promise 的引數函式的執行。如果不想執行後續程式碼,我們可以在resolvereject前面加上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方法

thencatch()方法返回的是一個Promise例項,存在以下三種情況

  • 當返回非Promise物件的值時,thencatch都生成一個狀態為fulfilled的Promise物件,並把該返回值傳入Promise鏈的下一環節
  • 當返回值為Promise物件時,生成的Promise物件的狀態由被返回的Promise物件決定,傳入Promise鏈下一環節的值也由這個被返回的Promise決定
  • 當Promise的回撥函式中丟擲錯誤時,thencatch都生成一個狀態為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最後的狀態,在執行完thencatch指定的回撥函式以後,都會執行finally方法指定的回撥函式。

finally方法的回撥函式不接受任何引數。

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

Promise.all()

Promise.all()方法用於將多個 Promise 例項,包裝成一個新的 Promise 例項。

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

上面程式碼中,Promise.all()方法接受一個數組作為引數,p1p2p3都是 Promise 例項,如果不是,就會先呼叫Promise.resolve方法,將引數轉為 Promise 例項,再進一步處理。另外,Promise.all()方法的引數可以不是陣列,但必須具有 Iterator 介面,且返回的每個成員都是 Promise 例項。

p的狀態由p1p2p3決定,分成兩種情況。

  • 只有p1p2p3的狀態都變成fulfilledp的狀態才會變成fulfilled,此時p1p2p3的返回值組成一個數組,傳遞給p的回撥函式。

  • 只要p1p2p3之中有一個被rejectedp的狀態就變成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正確用法