iT邦幫忙

1

React 學習筆記_11(JavaScript 中的Promise)

  • 分享至 

  • xImage
  •  

JaveScript 中的 Promise

Promise的含意

  • 所謂的 "promise" 代表為一個容器,保存著某個未來才會結束的事件(非同步)的結果。

  • Promise有兩個特點 :
    1.不受外界影響。Promise有三種狀態: "pending(進行中)", "fulfilled(已成功)" , "rejected(以失敗)",只有非同步操作的結果才能改變他的狀態,其他手段無法改變。

    2.狀態一旦改變就不會再變化。Promise狀態有兩種改變 : pending -> fulfilled , pending -> rejected。只要任何一種情況發生就不會再發生變化,此時稱為 "resolved(已完成)" ,就算再對她添加callback function也依然會得到這個結果。

基本用法 :

const promise = new Promise(function(resolve,rejected){
    if(/* Success */)
    {
        resolve(value);
    }
    else
    {
        reject(error);
    }
});

Promise接收一個function作為輸入參數,該function的兩個參數分別是 "resolve" , "reject"

resolve : 當promise從未完成變成成功(pending -> resolved)時調用,將操作的結果作為參數傳遞。
rejuct : 當promise從未完成變成失敗(pending -> reject)時調用,將操作失敗的錯誤報出並傳遞。

Promise生成後可以使用.then方法分別指定resolved與reject的callback function

promise.then(function(value){
    //Success
},function(error){
    //false
});

then可以接受兩個callback function作為參數,當promise變為resolved時呼叫第一個function,反之當變成reject時呼叫第二個function。

Example :

timeout = (ms) => {
    return new Promise((resolve,reject) =>{
    
        //將"done"當作參數傳遞給resolve
        setTimeout(resolve,ms,"done");
    });
}

//傳遞給resolve的參數因為操作成功所以可以適用then讀取到參數(done)
timeout(100).then((value) => {
    console.log(value); //done
});

若呼叫resolve或是reject時帶有參數,那麼他們的參數會被傳遞給callback function。reject的參數通常是error對象,表示發生的錯誤 ; resolve的參數除了正常的數值之外已可能是另一個promise

const p1 = new Promise(function (resolve, reject) {
  // ...
});

const p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
});

p1與p2都是promise,但是p2的resolve將p1做為參數,即一個非同步的操作會return另一個非同步的操作,這時p1的狀態會傳遞給p2,也就是說p1決定了p2的狀態,若p1的狀態是pending,則p2會等待p1的狀態改變;若p1已經是resolved或是rejected,那麼p2的callback function便會立刻執行。

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三秒後會變成reject;p2狀態在一秒之後會改變,resolve方法返回的是p1,由於p2返回的是另一個promise會導致自己的狀態無效,由p1的狀態決定了p2的狀,所以後面的.then都會變成針對 "p1" 的狀態,所以會觸發.catch console出Error:fail。

注意!! 若是調用了resolve或reject並不會結束promise的function執行。

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

若是調用了resolve(1)後,後面的console.log(2)仍然會執行並會先被console出來,因為callback function必須等到stack空了後才會執行,所以一般來說後續的操作應該放在.then裡面執行,在他們之前加上"return"以防有意外。

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面不會執行
  console.log(2);
})

Promise.prototype.then()

為promise添加狀態改變時的callback function,第一個參數是resolved狀態的callback function,第二的參數(可選)是rejected狀態的callback function。

then會return一個新的promise(不是原來的promise),因此可以採用鏈式的寫法,then後面再調用一個then

function timeout(ms) {
    return new Promise((resolve, reject) => {
        resolve(1);
    });
}

timeout(1000).then((value) => {
    console.log(value);
    return value + 10; // 將第一個callback的結果return給第二的callback當作輸入參數
}).then((value) => {
    console.log(value);
});

Promise.prototype.catch()

catch用於指定發生錯誤時的callback function,若一個Promise的非同步操作發生錯誤狀態變為rejected,則會調用catch所指定的callback function處理這個錯誤,若是then方法指定的callback function執行發生錯誤的話也會被catch捕獲到。

promise發生錯誤 :

function timeout(ms) {
    return new Promise((resolve, reject) => {
        resolve(AAA); //產生error
    });
}

timeout(1000).then((value) => {
    console.log("Success");
}).catch((error) =>{
    console.log(error)  //ReferenceError: AAA is not defined
})

then發生錯誤 :

function timeout(ms) {
    return new Promise((resolve, reject) => {
        resolve(1);
    });
}

timeout(1000).then((value) => {
    console.log(X); //造成錯誤
}).catch((error) =>{
    console.log(error) //ReferenceError: X is not defined
})

由於catch可以捕獲到promise與之後then的錯誤,所以建議對於reject來說使用catch,而不使用then的第二個參數。

Promise.prototype.finally()

finally用於不管Promise最後狀況為何都會執行的操作。(ES2018加入)

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

不論promise的狀態為何都會執行finally指定的callback function。
由於finally不接受任何輸入參數,所以無法知道promise的狀態為何,這鰾式finally裡面的操作式與狀態無關的,不依賴promise的執行結果。

Promise.prototype.all()

all用於將多個Promise包裝成一個新的Promise。

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

p的狀態由p1,p2,p3決定,分成兩種情況 :
1.當p1,p2,p3都變成fulfilled時,p才會是fulfilled,此時p1,p2,p3返回一個數組傳遞給p的callback function。
2.只要p1,p2,p3其中一個變成reject那麼p就會變成reject,此時第一個變成reject的的return會傳遞給P的callback funciton。

Example :

const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});

只有當6個promise的狀態都改變成fulfilled後,p的狀態才會改變為fulfilled,而若6個promise有其中一個變為reject那p的狀態就會變為reject。而當p的狀態改變後才會調用後面的callback function。

注意!!如果做為餐數的promise有定義自己的catch那麼一旦它變成rejected,並不會觸發到all.catch而是自己的catch

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e); //定義自己的catch

const p2 = new Promise((resolve, reject) => {
  throw new Error('報錯');
})
.then(result => result)
.catch(e => e); //定義自己的catch

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));

上面的程式中,p1狀態會變為resolved而p2的狀態會變為reject而進入自己的catch,當p2進入自己的catch後,由於執行完catch後狀態會變為resolved,所以對all而言兩個promise都是resolved,便不會進入到all.catch指定的callback function中。

Promise.prototype.race()

同樣是將多個promise包裝成一個新的promise但與all不同。

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

當p1,p2,p3任何一個發生狀態改變時,p的狀態就會跟著改變,首先改變狀態的promise會return一個參數傳入p的callback function中。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面的程式,若秒之內fatch方法無法返回結果,p的狀態就會變成reject(當一個狀態改變時,p狀態會跟著改變),從而觸發catch中的callback function。

Promise.prototype.resolve()

將現有的對象轉變為promise

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

resolve()的參數有四種 :
1.參數是一個promise : 若promise的參數是一個promise,則不做動作 return 一個promise

2.參數是一個thenable(具有then方法的object)

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

pormise.resolve會將這個object轉為peomise,然後立刻執行thenable的then

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

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

上面的程式中,thanable object的then執行後,p1狀態就變為resolved,從而執行p1的then的callback function並console出42。

3.參數不具有then的object或根本不是object,則promise.resolve返回一個新的promise狀態為resolved

4.不帶任何參數,會直接返回一個resolved狀態的promise,注意!! resolve()的promise是在本輪event loop結束時才執行,而不是下一輪event loop才執行

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

由於setTimeout在下一輪event loop才執行,而promise.resolved()是在本輪的event loop結束時執行,console.log("one")則是直接放入stack中立刻執行,所以最先輸出。

Promise.prototype.reject()

Promise.reject(reason)會return一個新的promise,而該promise的狀態為rejected。
注意!!Promise.reject的參數會做完reject的理由,變成後續catch的參數。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
  console.log(e);
  console.log(thenable);
})

/************ 
    true
    { then: [Function: then] }
    { then: [Function: then] }
*************/

參考資料 :
ECMAScript 6 入门


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言