這篇文章帶給大家的內容是關於promise物件的深入解析(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
js中的非同步,剛開始的時候都是用回呼函數實現的,所以如果非同步嵌套的話,就有出現回呼地獄,使得程式碼難以閱讀和難以維護,後來es6出現了promise,解決了回調地獄的問題。現在我們就自己寫程式碼實作promise,這樣才能深入理解promise的運作機制,對以後使用promise也能夠更加得心應手。開始之前可以先看下promise的官網promise/A
先來看下promise的用法
new Promise((resolve,reject)=>{ resolve(1); reject(11); }).then(res=>{ console.log(res); setTimeout(()=>{ return new Promise((resolve,reject)=>{ resolve(2) }) },1000) }).then(res2=>{ console.log(res2); });
控制台列印
1
...1s later
# 2
先分析下上面這段程式碼,先提出幾個問題
1.第一段resolve和reject都有,但是只輸出了1,為什麼?
2.then裡的res是如何取到resolve中的值的?
3.promise是如何做到鍊式呼叫的?
promise中有個狀態機的概念,先說下為什麼要有狀態機的概念呢,因為promise的狀態是單向變化的,有三種狀態,pending ,fullfilled,rejected,而這三種狀態只能從pending->fullfilled或pending->rejected這兩種形式,也就是說執行了fullfilled之後,就不會執行rejected。這就解釋了上面的第一個問題。
下面我們來看下具體實現的完整程式碼
const PENDING = 'PENDING'; const FULLFILLED = 'FULLFILLED'; const REJECTED = 'REJECTED'; class Promise{ constructor(fn){ this.status = PENDING;//状态 this.data = undefined;//返回值 this.defercb = [];//回调函数数组 //执行promise的参数函数,并把resolve和reject的this绑定到promise的this fn(this.resolve.bind(this),this.reject.bind(this)); } resolve(value){ if(this.status === PENDING){ //只能pending=>fullfied this.status = FULLFILLED; this.data = value; this.defercb.map(item=>item.onFullFilled()); } } reject(value){ if(this.status === PENDING){ //只能pending=>rejected this.status = REJECTED; this.data = value; this.defercb.map(item=>item.onRejected()); } } then(resolveThen,rejectThen){ //如果没有resolveThen方法,保证值可以穿透到下一个then里有resolveThen的方法中 resolveThen = typeof resolveThen === 'function' ? resolveThen : function(v) {return v}; rejectThen = typeof rejectThen === 'function' ? rejectThen : function(r) {return r}; //返回的都是promise对象,这样就可以保证链式调用了 switch(this.status){ case PENDING: return new Promise((resolve,reject)=>{ const onFullFilled = () => { const result = resolveThen(this.data);//这里调用外部then的resolveThen方法,将值传回去 //如果返回值是promise对象,执行then方法,取它的结果作为新的promise实例的结果,因为this.data会重新赋值 result instanceof Promise && result.then(resolve,reject); } const onRejected = ()=>{ const result = rejectThen(this.data); result instanceof Promise && result.then(resolve,reject); } this.defercb.push({onFullFilled,onRejected}); }); break; case FULLFILLED: return new Promise((resolve,reject)=>{ const result = resolveThen(this.data); result instanceof Promise && result.then(resolve,reject); resolve(result); }) break; case REJECTED: return new Promise((resolve,reject)=>{ const result = rejectThen(this.data); result instanceof Promise && result.then(resolve,reject); reject(result) }) break; } } }
運行下面的範例
new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000); }).then((res2) => { console.log(res2); return new Promise((resolve, reject) => { setTimeout(() => { resolve(2); }, 1000); }); }).then((res3) => { console.log(res3); return new Promise((resolve, reject) => { setTimeout(() => { resolve(3); }, 1000); }); }).then((res4) => { console.log(res4); });
控制台列印
...1s later
1
...1s later
2
...1s later
3
說明上面的實作是沒有問題的
不過還有一個問題,就是事件循環的順序問題,例如執行下面的程式碼
new Promise((resolve) => { resolve(); }) .then(() => { console.log('1'); }) .then(() => { console.log('2'); }); console.log('3');
並沒有像預想中輸出3,1,2,而是輸出了1,2,3,原因就是因為我們的這個Promise是在主執行緒中,沒有在下一個任務佇列中,可以加上settimeout解決這個問題,不過這也只是為了讓我們更好理解執行順序而已,然而實際上是promise是屬於微任務中的,而settimeout是屬於宏任務,還是不太一樣的
#以上是promise物件的深入解析(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!