JS原生引用類型解析7-Promise類型

1. 簡介

ES6引入了一個全新的對象Promise,用于表示一個異步操作的最終狀態(tài)(完成或失?。约捌浞祷氐闹?。Promise最直接的好處就是鏈?zhǔn)秸{(diào)用,另外在錯誤捕獲上也很方便。用同步的寫法解決異步問題,代碼直觀,易于理解維護(hù),解決了回調(diào)地獄的問題。關(guān)于Promise的詳細(xì)講解和更多用例我會開專門文章討論。這里我們主要看一下Promise及其原型的屬性和方法。

2. Promise對象創(chuàng)建

Promise對象使用new構(gòu)造函數(shù)創(chuàng)建?;臼褂梅椒ㄈ缦拢?/p>

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己部署。

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ù)是可選的,不一定要提供。這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)。

var promise = new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000, 'foo');
});
promise.then(function(val){
    console.log(val);  // 1000ms后輸出 ‘foo’
})

3. Promise構(gòu)造函數(shù)的屬性與方法

我們用Object.getOwnPropertyNames()方法獲取Promise構(gòu)造函數(shù)的所有屬性與方法。

Object.getOwnPropertyNames(Promise);
// (7) ["length", "name", "prototype", "all", "race", "resolve", "reject"]

發(fā)現(xiàn)一共有7個屬性和方法。

3.1 Promise構(gòu)造函數(shù)的屬性

Promise.length
長度總為1 (構(gòu)造器參數(shù)的數(shù)目)

Promise.name
名稱為"Promise"

Promise.prototype
指向Promise構(gòu)造函數(shù)的原型,可以為所有 Promise 類型的對象添加屬性。

3.2 Promise構(gòu)造函數(shù)的方法

Promise.all(iterable)
這個方法返回一個新的promise對象,該promise對象在iterable參數(shù)對象里所有的promise對象都成功的時候才會觸發(fā)成功,一旦有任何一個iterable里面的promise對象失敗則立即觸發(fā)該promise對象的失敗。這個新的promise對象在觸發(fā)成功狀態(tài)以后,會把一個包含iterable里所有promise返回值的數(shù)組作為成功回調(diào)的返回值,順序跟iterable的順序保持一致;如果這個新的promise對象觸發(fā)了失敗狀態(tài),它會把iterable里第一個觸發(fā)失敗的promise對象的錯誤信息作為它的失敗錯誤信息。Promise.all方法常被用于處理多個promise對象的狀態(tài)集合。(可以參考jQuery.when方法---MDN Promise譯者注)

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

當(dāng)然,當(dāng)參數(shù)不包含 Promise 時, 該方法返回完成(resolve),但這顯然沒有什么意義。

Promise.race(iterable)
當(dāng)iterable參數(shù)里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數(shù)調(diào)用父promise綁定的相應(yīng)句柄,并返回該promise對象。

var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, 'one');
});

var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then(function(value) {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"

Promise.reject(reason)
返回一個狀態(tài)為失敗的Promise對象,并將給定的失敗信息reason(Promise被拒絕的原因)傳遞給對應(yīng)的處理方法。此處使用Error實例的reason對調(diào)試和選擇性錯誤捕捉很有幫助。

Promise.reject("Testing static reject").then(function(reason) {
  // 未被調(diào)用
}, function(reason) {
  console.log(reason); // "Testing static reject"
});

Promise.reject(new Error("fail")).then(function(error) {
  // 未被調(diào)用
}, function(error) {
  console.log(error); // 堆棧跟蹤
  /* Error: fail
    at <anonymous>:7:16 */
});

Promise.resolve(value)
返回一個狀態(tài)由給定value決定的Promise對象。如果該值是一個Promise對象,則直接返回該對象;如果該值是thenable(即,帶有then方法的對象),返回的Promise對象的最終狀態(tài)由then方法執(zhí)行決定;否則的話(該value為空,基本類型或者不帶then方法的對象),返回的Promise對象狀態(tài)為fulfilled,并且將該value傳遞給對應(yīng)的then方法。通常而言,如果你不知道一個值是否是Promise對象,使用Promise.resolve(value) 來返回一個Promise對象,這樣就能將該value以Promise對象形式使用。

用法如下:

  1. Promise.resolve(promise);
    直接返回該對象promise

  2. Promise.resolve(thenable);
    返回一個最終狀態(tài)由then方法執(zhí)行決定的Promise對象

  3. Promise.resolve(value)
    value為空,基本類型,或者不帶then方法的對象,返回狀態(tài)為fulfilled的Promise對象,并且將該value傳遞給對應(yīng)的then方法

基本用法示例:

var promise1 = Promise.resolve([1, 2, 3]);

promise1.then(function(value) {
  console.log(value);
  // expected output: Array [1, 2, 3]
});

4. Promise原型對象的屬性與方法

我們用Object.getOwnPropertyNames()方法獲取Promise原型對象的所有屬性與方法。

Object.getOwnPropertyNames(Promise.prototype);
// (4) ["constructor", "then", "catch", "finally"]

發(fā)現(xiàn)一共有4個屬性和方法。

4.1 Promise原型對象的屬性

Promiset.prototype.constructor
指向構(gòu)造函數(shù)Promise

4.2 Promise原型對象的方法

Promise.prototype.then(onFullfilled, onRejected)
它最多需要有兩個參數(shù):Promise 的接受(fulfillment)和拒絕(rejection)情況的回調(diào)函數(shù)。返回一個新的 Promise,該Promise將以回調(diào)的返回值來resolve。

語法:

p.then(onFulfilled, onRejected);

p.then(function(value) {
   // fulfillment
  }, function(reason) {
  // rejection
});

參數(shù):

  • onFulfilled
    當(dāng)Promise變成接受狀態(tài)(fulfillment)時,該參數(shù)作為回調(diào)函數(shù)被調(diào)用。該函數(shù)有一個參數(shù),即接受的值(the fulfillment value)。
  • onRejected
    當(dāng)Promise變成拒絕狀態(tài)(rejection )時,該參數(shù)作為回調(diào)函數(shù)被調(diào)用。該函數(shù)有一個參數(shù),即拒絕的原因(the rejection reason)。

返回值:

then方法返回一個Promise。而它的行為與then中的回調(diào)函數(shù)的返回值有關(guān):

  • 如果then中的回調(diào)函數(shù)返回一個值,那么then返回的Promise將會成為接受狀態(tài),并且將返回的值作為接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值。

  • 如果then中的回調(diào)函數(shù)拋出一個錯誤,那么then返回的Promise將會成為拒絕狀態(tài),并且將拋出的錯誤作為拒絕狀態(tài)的回調(diào)函數(shù)的參數(shù)值。

  • 如果then中的回調(diào)函數(shù)返回一個已經(jīng)是接受狀態(tài)的Promise,那么then返回的Promise也會成為接受狀態(tài),并且將那個Promise的接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值作為該被返回的Promise的接受狀態(tài)回調(diào)函數(shù)的參數(shù)值。

  • 如果then中的回調(diào)函數(shù)返回一個已經(jīng)是拒絕狀態(tài)的Promise,那么then返回的Promise也會成為拒絕狀態(tài),并且將那個Promise的拒絕狀態(tài)的回調(diào)函數(shù)的參數(shù)值作為該被返回的Promise的拒絕狀態(tài)回調(diào)函數(shù)的參數(shù)值。

  • 如果then中的回調(diào)函數(shù)返回一個未定狀態(tài)(pending)的Promise,那么then返回Promise的狀態(tài)也是未定的,并且它的終態(tài)與那個Promise的終態(tài)相同;同時,它變?yōu)榻K態(tài)時調(diào)用的回調(diào)函數(shù)參數(shù)與那個Promise變?yōu)榻K態(tài)時的回調(diào)函數(shù)的參數(shù)是相同的。

注意:

如果忽略針對某個狀態(tài)的回調(diào)函數(shù)參數(shù),或者提供非函數(shù) (nonfunction) 參數(shù),那么 then 方法將會丟失關(guān)于該狀態(tài)的回調(diào)函數(shù)信息,但是并不會產(chǎn)生錯誤。如果調(diào)用 then 的 Promise 的狀態(tài)(fulfillment 或 rejection)發(fā)生改變,但是 then 中并沒有關(guān)于這種狀態(tài)的回調(diào)函數(shù),那么 then 將創(chuàng)建一個沒有經(jīng)過回調(diào)函數(shù)處理的新 Promise 對象,這個新 Promise 只是簡單地接受調(diào)用這個 then 的原 Promise 的終態(tài)作為它的終態(tài)。

用法示例:

var promise1 = new Promise(function(resolve, reject) {
  resolve('Success!');
});

promise1.then(function(value) {
  console.log(value);
  // expected output: "Success!"
});

Promise.catch(onRejected)
添加一個拒絕(rejection) 回調(diào)到當(dāng)前 promise, 返回一個新的promise。當(dāng)這個回調(diào)函數(shù)被調(diào)用,新 promise 將以它的返回值來resolve。它的行為與調(diào)用Promise.prototype.then(undefined, onRejected) 相同。
語法:

p.catch(onRejected);

p.catch(function(reason) {
   // 拒絕
});

參數(shù):

  • onRejected
    當(dāng)Promise 被拒絕時,被調(diào)用的一個Function。該函數(shù)擁有一個參數(shù):
  • reason
    拒絕的原因。

返回值:
一個Promise。

示例:
有三種常見的使用情況:

  1. 使用鏈?zhǔn)秸Z句的 catch方法:
var p1 = new Promise(function(resolve, reject) {
  resolve('Success');
});

p1.then(function(value) {
  console.log(value); // "成功!"
  throw 'oh, no!';
}).catch(function(e) {
  console.log(e); // "oh, no!"
}).then(function(){
  console.log('after a catch the chain is restored');
}, function () {
  console.log('Not fired due to the catch');
});

// 以下行為與上述相同
p1.then(function(value) {
  console.log(value); // "成功!"
  return Promise.reject('oh, no!');
}).catch(function(e) {
  console.log(e); // "oh, no!"
}).then(function(){
  console.log('after a catch the chain is restored');
}, function () {
  console.log('Not fired due to the catch');
});
  1. 捕獲拋出的錯誤
// 拋出一個錯誤,大多數(shù)時候?qū)⒄{(diào)用catch方法
var p1 = new Promise(function(resolve, reject) {
  throw 'Uh-oh!';
});

p1.catch(function(e) {
  console.log(e); // "Uh-oh!"
});

// 在異步函數(shù)中拋出的錯誤不會被catch捕獲到
var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

p2.catch(function(e) {
  console.log(e); // 不會執(zhí)行
});

// 在resolve()后面拋出的錯誤會被忽略
var p3 = new Promise(function(resolve, reject) {
  resolve();
  throw 'Silenced Exception!';
});

p3.catch(function(e) {
   console.log(e); // 不會執(zhí)行
});
  1. 如果已決議
//創(chuàng)建一個新的 Promise ,且已決議
var p1 = Promise.resolve("calling next");

var p2 = p1.catch(function (reason) {
    //這個方法永遠(yuǎn)不會調(diào)用
    console.log("catch p1!");
    console.log(reason);
});

p2.then(function (value) {
    console.log("next promise's onFulfilled"); /* next promise's onFulfilled */
    console.log(value); /* calling next */
}, function (reason) {
    console.log("next promise's onRejected");
    console.log(reason);
});

Promise.prototype.finally(onFinally)
添加一個事件處理回調(diào)于當(dāng)前promise對象,并且在原promise對象解析完畢后,返回一個新的promise對象?;卣{(diào)會在當(dāng)前promise運行完畢后被調(diào)用,無論當(dāng)前promise的狀態(tài)是完成(fulfilled)還是失敗(rejected)

注意:
finally() 雖然與 .then(onFinally, onFinally) 類似,它們不同的是:

  • 調(diào)用內(nèi)聯(lián)函數(shù)時,不需要多次聲明該函數(shù)或為該函數(shù)創(chuàng)建一個變量保存它。
    由于無法知道promise的最終狀態(tài),所以finally的回調(diào)函數(shù)中不接收任何參數(shù),它僅用于無論最終結(jié)果如何都要執(zhí)行的情況。
  • 與Promise.resolve(2).then(() => {}, () => {}) (resolved的結(jié)果為undefined)不同,Promise.resolve(2).finally(() => {}) resolved的結(jié)果為 2。
  • 同樣,Promise.reject(3).then(() => {}, () => {}) (resolved 的結(jié)果為undefined), Promise.reject(3).finally(() => {}) rejected 的結(jié)果為 3。

用法示例:
一個典型的用法,在發(fā)出請求時,頁面的loading效果開啟,然后不管返回的結(jié)果是完成(fulfilled)還是失敗(rejected),都會執(zhí)行onFinally將loading效果取消。

let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); })
  .finally(function() { isLoading = false; });
  1. Promise實例對象的屬性與方法
    我們用Object.getOwnPropertyNames()方法獲取Promise實例對象的所有屬性與方法。
var p = new Promise(function(resolve, reject) {
    resolve('success');
})
Object.getOwnPropertyNames(p);  // []

我們發(fā)現(xiàn)實例本身沒有綁定屬性與方法。

參考

MDN-Promise
Promise 對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容