1.Promise介紹 Promise最早是社區提出和實現,後面ES6將其寫入標準,並原生提供Promise對象,是一種非同步編程的解決方案,具體的概念大家可以去查看相關的資料。傳統上處理非同步都是以callback回調函數的方式完成,但是當回調嵌套的太多,便會使程式很難理解,如下所示 如果後面還有基於 ...
1.Promise介紹
Promise最早是社區提出和實現,後面ES6將其寫入標準,並原生提供Promise對象,是一種非同步編程的解決方案,具體的概念大家可以去查看相關的資料。傳統上處理非同步都是以callback回調函數的方式完成,但是當回調嵌套的太多,便會使程式很難理解,如下所示
function a(cb){ console.log('a...'); cb('a'); } function b(cb){ console.log('b...'); cb('b'); } function c(cb){ console.log('c...'); cb('c'); } a(() => b( () => c( () => {}) )); a... b... c...
如果後面還有基於C函數的輸出結果的邏輯,回調將是很大的困惱。
而使用Promise來處理這種非同步回調將會非常的直觀,Promise以同步的操作完成非同步的處理,如下所示
function a(){ console.log('a...'); return 'a output'; } function b(res){ console.log('b...'); console.log('get A:'+res); return "b output"; } function c(res){ console.log('c...'); console.log('get B:'+res); } new Promise((resolve,reject) => {setTimeout(resolve,1000)}).then(a).then(b).then(c); Promise { <state>: "pending" } a... b... get A:a output c... get B:b output
2.Promise的用法
Promise是一個構造函數,接收一個函數參數,該函數接收兩個函數作為參數,分別是resolve,reject。這兩個函數由Promise自己提供,無需自己部署。resolve函數將Promise的狀態從'Pending'轉為'Resolved',reject函數將狀態從'Pending'轉為'rejected'。
基本語法,如下所示
new Promise((resolve,reject) => { if(success){ //非同步處理成功 resolve(); }else{ reject(); } }).then(onFulfilled,onRejected);
創建一個Promise實例,如下所示
var promise = new Promise((resolve,reject) => {console.log('hehe');resolve('resolved')}); promise.then((val) => console.log(val)) console.log('current'); hehe current resolved
resolve函數與reject函數調用時可以傳遞參數,一般給reject函數傳遞Error的實例,用於指出拋出的錯誤,傳給reject函數的參數可以是一般的數值,也可以是Promise的實例,這時該Promise的實例狀態由參數Promise實例的狀態決定,如下所示
var p1 = new Promise((resolve,reject) => { setTimeout(()=>{console.log('1秒後的 p1');resolve(p2);},1000); }); var p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{console.log('2秒後的 p2');resolve('p2');},2000); }); p1.then((val)=>console.log(val)); Promise { <state>: "pending" } 1秒後的 p1 2秒後的 p2 p2
如上所示,Promise是創建就立即執行,且then方法的回調函數將在當前腳本所有同步語句執行完執行,但是比定時器的隊列優先順序要高,如下所示
setTimeout(()=>console.log('settimeouting...'),0); var promise = new Promise((resolve,reject) => { console.log('promise constructing...'); reject(new Error('error')); }); promise.then(null,(reason)=>console.log(reason)); promise constructing... Promise { <state>: "pending" } Error: error 堆棧跟蹤: promise<@debugger eval code:4:9 @debugger eval code:2:15 settimeouting...
Promise的原型方法then,該方法為promise實例添加狀態改變時的回調函數,第一個參數是狀態為resolved時的回調,第二個參數是狀態為rejected的回調。如下所示
var p3 = new Promise((resolve,reject) => { console.log('p3'); resolve('p3'); }); var p4 = new Promise((resolve,reject) => { console.log('p4'); setTimeout(()=>resolve('p4'),1500); }); p3.then(()=>p4).then((result) => console.log(result));//這裡需要註意一點,如果then方法返回一個Promise實例,則下一個then回調需要等待該Promise實例的狀態改變才會執行。 p3 p4 Promise { <state>: "pending" } p4
Promise內部拋出的錯誤不會被捕獲(Chrome瀏覽器除外),除非使用catch方法,如果在resolve之前拋出,則resolve不會執行,相反則相當於沒有拋出錯誤,如下所示
var promise = new Promise((resolve,reject) => { console.log('promise constructing...'); throw new Error('error'); resolve('haha'); }); promise.then((val)=>console.log(val)); promise constructing... ------------------------------------------------------------- var promise = new Promise((resolve,reject) => { console.log('promise constructing...'); resolve('haha'); throw new Error('error'); }); promise.then((val)=>console.log(val)); promise constructing... haha
使用Promise的原型方法catch捕獲錯誤,該方法其實是.then(null,reject)的別名,如下所示
var promise = new Promise((resolve,reject) => { console.log('promise constructing...'); throw new Error('error'); resolve('haha'); }); promise.then((val)=>console.log(val)).catch((error)=>console.log(error)); promise constructing... Promise { <state>: "pending" } Error: error 堆棧跟蹤: promise<@debugger eval code:3:8 @debugger eval code:1:19
Promise的錯誤捕獲可以使用then方法的第二個參數指定reject的回調函數,也可以使用Promise.prototype.catch方法捕獲,但是一般推薦使用方法二,因為catch不僅能捕獲到promise內部拋出的錯誤,then方法中的錯誤也能被捕獲。但是在調用resolve方法之後,再拋出錯誤,不會被捕獲,相當於沒有拋出錯誤,如下所示
var promise = new Promise((resolve,reject) => { throw new Error('promise error'); }); promise.then(null,(err)=>console.log(err)); Promise { <state>: "pending" } Error: promise error 堆棧跟蹤: promise<@debugger eval code:2:9 @debugger eval code:1:19 ---------------------------catch method------------------- var promise = new Promise((resolve,reject) => { reject(new Error('promise error')); }); promise.catch((error)=>console.log(error)); Promise { <state>: "pending" } Error: promise error 堆棧跟蹤: promise<@debugger eval code:2:9 @debugger eval code:1:19 ---------------------------then方法中報錯------------------- var p5 = new Promise((resolve,reject) => { resolve('hehe'); }); p5.then((val)=>val+x).catch((err)=>console.log(err)); Promise { <state>: "pending" } ReferenceError: x is not defined 堆棧跟蹤: @debugger eval code:4:16
catch方法也是返回一個Promise,因此在catch後面還可以接著使用鏈式方法then,但這時then方法報錯將不會被前面的catch捕獲,如下所示
var p5 = new Promise((resolve,reject) => { resolve('hehe'); }); p5.then((val)=>val+x).catch((err)=>console.log(err)).then((val)=>y+3); Promise { <state>: "pending" } ReferenceError: x is not defined 堆棧跟蹤: @debugger eval code:4:16
3.Promise.all
Promise.all方法可以將多個Promise實例[p1,p2,p3...]包裝成一個Promise對象A,A的狀態由p1,p2,p3...決定:如果p1,p2,p3...的狀態都變為onfulfilled,A的狀態才變為onfulfilled,且p1,p2,p3...的非同步操作結果組成一個數組傳給回調函數;但凡p1,p2,p3...中有一個狀態變為了rejected,A的狀態立即變為rejected,且第一個rejected的返回值會傳給回調函數,如下所示
var p1 = new Promise((resolve,reject) => { console.log('p1...'); setTimeout(()=>resolve('p1 resolved...'),1000); }); var p2 = new Promise((resolve,reject) => { console.log('p2...'); setTimeout(()=>resolve('p2 resolved...'),2000); }); var p3 = new Promise((resolve,reject) => { console.log('p3...'); setTimeout(()=>resolve('p3 resolved...'),3000); }); Promise.all([p1,p2,p3]).then((args)=>{console.log('3S後的輸出:');for(let v of args){console.log(v);}}); p1... p2... p3... 3S後的輸出: p1 resolved... p2 resolved... p3 resolved... ------------------------ 有一個Promise實例變為rejected --------------- var p1 = new Promise((resolve,reject) => { console.log('p1...'); setTimeout(()=>resolve('p1 resolved...'),1000); }); var p2 = new Promise((resolve,reject) => { console.log('p2...'); setTimeout(()=>resolve('p2 resolved...'),2000); }); var p3 = new Promise((resolve,reject) => { console.log('p3...'); setTimeout(()=>resolve('p3 resolved...'),3000); }); var p4 = new Promise((resolve,reject) => { console.log('p4...'); reject("p4 rejected..."); }); Promise.all([p1,p2,p3,p4]).then((args)=>console.log(args),(reason) => console.log(reason)); p1... p2... p3... p4... Promise { <state>: "pending" } p4 rejected...
4.Promise.race
同Promise.all一樣,該方法也是將多個Promise實例[p1,p2,p3...]包裝成一個新的Promise實例B。"race"的字面意思就是競爭,所以[p1,p2,p3...]誰的狀態先改變,B的狀態立即改變一樣的狀態且第一個改變狀態的promise實例的返回值傳遞給回調函數,如下所示
var p1 = new Promise((resolve,reject) => { console.log('p1...'); setTimeout(()=>resolve('p1 resolved...'),1000); }); var p2 = new Promise((resolve,reject) => { console.log('p2...'); setTimeout(()=>resolve('p2 resolved...'),2000); }); var p3 = new Promise((resolve,reject) => { console.log('p3...'); setTimeout(()=>resolve('p3 resolved...'),3000); }); Promise.race([p1,p2,p3]).then((val)=>console.log(val),(reason)=>console.log(reason)); p1... p2... p3... Promise { <state>: "pending" } p1 resolved...
5.Promise.resolve與Promise.reject
Promise.resolve是將給定的參數轉換成一個Promise對象,如下所示
Promise.resolve(arg);//等同於new Promise((resolve)=>resolve(arg)) omise.resolve('resolve convert...').then((val)=>console.log(val)); Promise { <state>: "pending" } resolve convert...
根據給定的參數類型不同,Promise.resolve有不同的動作:
A、參數為Promise實例,則直接返回該實例
B、參數為空,執行返回一個resolved狀態的新Promise實例
C、參數為普通的數值或對象,則直接返回一個resolved狀態的新Promise實例,如下所示
Promise.resolve({x:1}).then((val)=>console.log(val)); Promise { <state>: "pending" } Object { x: 1 }
D、參數為thenable對象,既對象實現了then方法,此時返回一個新Promise實例並立即執行對象then方法,如下所示
var thenableObj = { then(resolve,reject){ reject('test...'); } }; Promise.resolve(thenableObj).then(null,(reason)=>console.log(reason)); Promise { <state>: "pending" } test...
Promise.reject與Promise.resolve的用法一致,只是返回的Promise實例的狀態為rejected,如下所示
Promise.reject('reject convert...').then(null,(reason) => console.log(reason)); Promise { <state>: "pending" } reject convert...
6.給原生Promise對象擴展方法,如下所示
//一下兩個函數來自raunyifeng博客 Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected) .catch(function (reason) { setTimeout(() => { throw reason }, 0); }); }; 該方法只要執行就可以捕獲前面調用鏈中的錯誤 Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); }; 該方法不管Promise的實例的狀態是否改變都會執行回調函數