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對象形式使用。
用法以下:
Promise.resolve(promise);
直接返回該對象promisePromise.resolve(thenable);
返回一個最終狀態由then方法執行決定的Promise對象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。
示例:
有三種常見的使用狀況:
- 使用鏈式語句的 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'); });
- 捕獲拋出的錯誤
// 拋出一個錯誤,大多數時候將調用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); // 不會執行 });
- 若是已決議
//建立一個新的 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; });
- Promise實例對象的屬性與方法
咱們用Object.getOwnPropertyNames()方法獲取Promise實例對象的全部屬性與方法。
var p = new Promise(function(resolve, reject) { resolve('success'); }) Object.getOwnPropertyNames(p); // []
咱們發現實例自己沒有綁定屬性與方法。
參考
本文同步分享在 博客「love丁酥酥」(JianShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。