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

1. 簡介

ES6引入了一個全新的對象Promise,用於表示一個異步操做的最終狀態(完成或失敗),以及其返回的值。Promise最直接的好處就是鏈式調用,另外在錯誤捕獲上也很方便。用同步的寫法解決異步問題,代碼直觀,易於理解維護,解決了回調地獄的問題。關於Promise的詳細講解和更多用例我會開專門文章討論。這裏咱們主要看一下Promise及其原型的屬性和方法。php

2. Promise對象建立

Promise對象使用new構造函數建立。基本使用方法以下:es6

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

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

Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由 JavaScript 引擎提供,不用本身部署。json

then方法能夠接受兩個回調函數做爲參數。第一個回調函數是Promise對象的狀態變爲resolved時調用,第二個回調函數是Promise對象的狀態變爲rejected時調用。其中,第二個函數是可選的,不必定要提供。這兩個函數都接受Promise對象傳出的值做爲參數。數組

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

3. Promise構造函數的屬性與方法

咱們用Object.getOwnPropertyNames()方法獲取Promise構造函數的全部屬性與方法。promise

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

發現一共有7個屬性和方法。ruby

3.1 Promise構造函數的屬性

Promise.length
長度總爲1 (構造器參數的數目)app

Promise.name
名稱爲"Promise"異步

Promise.prototype
指向Promise構造函數的原型,能夠爲全部 Promise 類型的對象添加屬性。svg

3.2 Promise構造函數的方法

Promise.all(iterable)
這個方法返回一個新的promise對象,該promise對象在iterable參數對象裏全部的promise對象都成功的時候纔會觸發成功,一旦有任何一個iterable裏面的promise對象失敗則當即觸發該promise對象的失敗。這個新的promise對象在觸發成功狀態之後,會把一個包含iterable裏全部promise返回值的數組做爲成功回調的返回值,順序跟iterable的順序保持一致;若是這個新的promise對象觸發了失敗狀態,它會把iterable裏第一個觸發失敗的promise對象的錯誤信息做爲它的失敗錯誤信息。Promise.all方法常被用於處理多個promise對象的狀態集合。(能夠參考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"]

固然,當參數不包含 Promise 時, 該方法返回完成(resolve),但這顯然沒有什麼意義。

Promise.race(iterable)
當iterable參數裏的任意一個子promise被成功或失敗後,父promise立刻也會用子promise的成功返回值或失敗詳情做爲參數調用父promise綁定的相應句柄,並返回該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)
返回一個狀態爲失敗的Promise對象,並將給定的失敗信息reason(Promise被拒絕的緣由)傳遞給對應的處理方法。此處使用Error實例的reason對調試和選擇性錯誤捕捉頗有幫助。

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

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

Promise.resolve(value)
返回一個狀態由給定value決定的Promise對象。若是該值是一個Promise對象,則直接返回該對象;若是該值是thenable(即,帶有then方法的對象),返回的Promise對象的最終狀態由then方法執行決定;不然的話(該value爲空,基本類型或者不帶then方法的對象),返回的Promise對象狀態爲fulfilled,而且將該value傳遞給對應的then方法。一般而言,若是你不知道一個值是不是Promise對象,使用Promise.resolve(value) 來返回一個Promise對象,這樣就能將該value以Promise對象形式使用。

用法以下:

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

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

  3. Promise.resolve(value)
    value爲空,基本類型,或者不帶then方法的對象,返回狀態爲fulfilled的Promise對象,而且將該value傳遞給對應的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"]

發現一共有4個屬性和方法。

4.1 Promise原型對象的屬性

Promiset.prototype.constructor
指向構造函數Promise

4.2 Promise原型對象的方法

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

語法:

p.then(onFulfilled, onRejected);

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

參數:

  • onFulfilled
    當Promise變成接受狀態(fulfillment)時,該參數做爲回調函數被調用。該函數有一個參數,即接受的值(the fulfillment value)。
  • onRejected
    當Promise變成拒絕狀態(rejection )時,該參數做爲回調函數被調用。該函數有一個參數,即拒絕的緣由(the rejection reason)。

返回值:

then方法返回一個Promise。而它的行爲與then中的回調函數的返回值有關:

  • 若是then中的回調函數返回一個值,那麼then返回的Promise將會成爲接受狀態,而且將返回的值做爲接受狀態的回調函數的參數值。

  • 若是then中的回調函數拋出一個錯誤,那麼then返回的Promise將會成爲拒絕狀態,而且將拋出的錯誤做爲拒絕狀態的回調函數的參數值。

  • 若是then中的回調函數返回一個已是接受狀態的Promise,那麼then返回的Promise也會成爲接受狀態,而且將那個Promise的接受狀態的回調函數的參數值做爲該被返回的Promise的接受狀態回調函數的參數值。

  • 若是then中的回調函數返回一個已是拒絕狀態的Promise,那麼then返回的Promise也會成爲拒絕狀態,而且將那個Promise的拒絕狀態的回調函數的參數值做爲該被返回的Promise的拒絕狀態回調函數的參數值。

  • 若是then中的回調函數返回一個未定狀態(pending)的Promise,那麼then返回Promise的狀態也是未定的,而且它的終態與那個Promise的終態相同;同時,它變爲終態時調用的回調函數參數與那個Promise變爲終態時的回調函數的參數是相同的。

注意:

若是忽略針對某個狀態的回調函數參數,或者提供非函數 (nonfunction) 參數,那麼 then 方法將會丟失關於該狀態的回調函數信息,可是並不會產生錯誤。若是調用 then 的 Promise 的狀態(fulfillment 或 rejection)發生改變,可是 then 中並無關於這種狀態的回調函數,那麼 then 將建立一個沒有通過回調函數處理的新 Promise 對象,這個新 Promise 只是簡單地接受調用這個 then 的原 Promise 的終態做爲它的終態。

用法示例:

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

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

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

p.catch(onRejected);

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

參數:

  • onRejected
    當Promise 被拒絕時,被調用的一個Function。該函數擁有一個參數:
  • reason
    拒絕的緣由。

返回值:
一個Promise。

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

  1. 使用鏈式語句的 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. 捕獲拋出的錯誤
// 拋出一個錯誤,大多數時候將調用catch方法
var p1 = new Promise(function(resolve, reject) {
  throw 'Uh-oh!';
});

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

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

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

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

p3.catch(function(e) {
   console.log(e); // 不會執行
});
  1. 若是已決議
//建立一個新的 Promise ,且已決議
var p1 = Promise.resolve("calling next");

var p2 = p1.catch(function (reason) {
    //這個方法永遠不會調用
    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)
添加一個事件處理回調於當前promise對象,而且在原promise對象解析完畢後,返回一個新的promise對象。回調會在當前promise運行完畢後被調用,不管當前promise的狀態是完成(fulfilled)仍是失敗(rejected)

注意:
finally() 雖然與 .then(onFinally, onFinally) 相似,它們不一樣的是:

  • 調用內聯函數時,不須要屢次聲明該函數或爲該函數建立一個變量保存它。
    因爲沒法知道promise的最終狀態,因此finally的回調函數中不接收任何參數,它僅用於不管最終結果如何都要執行的狀況。
  • 與Promise.resolve(2).then(() => {}, () => {}) (resolved的結果爲undefined)不一樣,Promise.resolve(2).finally(() => {}) resolved的結果爲 2。
  • 一樣,Promise.reject(3).then(() => {}, () => {}) (resolved 的結果爲undefined), Promise.reject(3).finally(() => {}) rejected 的結果爲 3。

用法示例:
一個典型的用法,在發出請求時,頁面的loading效果開啓,而後無論返回的結果是完成(fulfilled)仍是失敗(rejected),都會執行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);  // []

咱們發現實例自己沒有綁定屬性與方法。

參考

MDN-Promise
Promise 對象

本文同步分享在 博客「love丁酥酥」(JianShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索