首頁 > web前端 > js教程 > promise物件的深入解析(附範例)

promise物件的深入解析(附範例)

不言
發布: 2018-11-23 14:49:00
轉載
2217 人瀏覽過

這篇文章帶給大家的內容是關於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中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板