Promise 規範解讀及實現細節 (一)

1. macrotask (宏任務)和 microtask (微任務)

這兩個是指兩個隊列,腳本總體代碼、setTimeout、setInterval、setImmediate、I/O、的回調及UI渲染都會被加入到 macrotask 隊列中, process.nextTick回調、Promise(瀏覽器實現)、MutationObserver回調都會被加入到microtask隊列。windows

  • 1 當瀏覽器進入事件循環時,先去看macrotask中有沒有具有執行條件的任務
  • 2 若是有則選取一個(只選一個)執行
  • 3 沒有則去看microtask隊列有沒有具有執行條件的任務
  • 4 有則所有執行(所有執行)
  • 5 去更新頁面
  • 6 而後重複以上行爲

2. Promise 介紹

Promise主要的功能是將異步執行結果保存起來,從而使得咱們再也不用回調的方式去處理異步任務的結果,咱們經過一個回調將異步任務的結果保存起來,在之後某個時刻經過各類姿式去使用它new romise(excutor).then(function(result)}).then(function(result){});的方式去執行異步任務,Promise 是經過回調來實現的,但它將回調統一,使得咱們不在去直接接觸回調,用看似有點像同步的方式去寫異步任務,也就僅僅是這樣而已,並無什麼新東西,這種同步方式只能說是相似同步,下一篇會講寫法更接近同步、更加有建術的 generator 。promise

3. Promise 的構造函數應該是這樣的

function Promise(excutor) {
    var self = this;
    var status = 'PENDING';  //Promise的當前狀態
    var data = undefined;  //當前excutor(就是用戶的異步任務)的執行結果
    self.onResolvedCallback = [];  //excutor 執行成功後回調函數隊列
    self.onRejectedCallback = [];  //excutor 執行失敗後回調函數隊列

    function resolve(value) {  
        // TUDO 異步任務執行成功後的行爲
        // 1 改變當前promise的status值 調用該方法 PENDING->RESOLVED
        // 2 執行onResolvedCallback隊列裏面的全部回調函數並傳遞異步任務的異步處理結果
        // onResolvedCallback[i](data)
    }

    function reject(value) {
        // TUDO 異步任務執行失敗後的行爲
        // 1 改變當前promise的status值 調用該方法 PENDING->REJECTED
        // 2 執行onRejectedCallback隊列裏面的全部回調函數並傳遞異步任務的異步處理結果
        // onRejectedCallback[i](data)
    }

    excutor(resolve, reject);  //執行用戶添加的任務
}

Promise.prototype.then = function (onResolved, onRejected) {
    // onResolved異步任務執行成功的回調
    // onRejected異步任務執行失敗後的回調
}

看起來就是這樣的 具體使用方式
new Promise(function(resolve,reject){
    setTimeout(function(result){
        // resolve(result) 或reject(result) 根據返回值決定
        //這裏咱們確實仍是寫了回調,可是咱們在回調裏面沒有作太多的事件
        //僅僅是傳遞了異步處理結果 想一想若是,咱們在這裏又要作依賴於result的
        //異步任務會怎樣,回調地獄,代碼結構混亂,不容易讀
    });
}).then(function(result){}) //這裏的result是從上面的那個回調裏面傳遞的,是同一個值

4. 具體實現

(1) resolve 函數和 reject 函數瀏覽器

function Promise(excutor) {
    var self = this;
    var status = 'PENDING';  //Promise的當前狀態
    var data = undefined;  //當前excutor(就是用戶的異步任務)的執行結果
    self.onResolvedCallback = [];  //excutor 執行成功後回調函數隊列
    self.onRejectedCallback = [];  //excutor 執行失敗後回調函數隊列

    function resolve(value) {  
        // TUDO 異步任務執行成功後的行爲
        if(self.status === 'PENDING') {
            self.status = 'REJECTED'; // 改變當前promise的status值 調用該方法 PENDING->RESOLVED
            self.data = value;  // 更新當前的data
            for(var i in self.onResolvedCallback) {
                self.onResolvedCallback[i](self.data);  //執行onResolvedCallback隊列裏面的全部回調函數並傳遞異步任務的異步處理結果
            }
        }
    }

    function reject(reson) {
        // TUDO 異步任務執行失敗後的行爲
        if(self.status === 'PENDING') {
            self.status = 'REJECTED';
            self.data = reson;
            for(var i in self.onRejectedCallback) {
                self.onRejectedCallback[i](self.data);
            }
        }
    }
    try {
        excutor(resolve.bind(this), reject.bind(this));  //執行用戶添加的任務
        /*這裏爲何要經過經過 bind 來綁定上下文呢,主要是 resolve 依賴當
        前 promise的內部屬性,在excutor函數體內,咱們是經過 resolve(data)
        或者reject(reson) 的方式來調用經過這種方式調用 在非嚴格模式
        resolve或reject的上下文是windows,在嚴格模式下是undefined*/
    } catch(e) {
        reject.bind(this)(e);  //出現異常則經過reject向後傳遞
    }
}

(2) then 方法的實現異步

then方法應當具有以下功能
(1) then方法必須返回一個Promise對象,也就是,對應用戶添加的邏輯要進行包裝
(2) 同一個對象可屢次調用then來添加邏輯,而且會按照添加順序執行函數

//例如:

var promise = new Promise(function(resolve, reject) {
    //異步操做 而且在回調中調用了 resolve或者 reject將異步處理結果傳遞出去
});
promise.then(function(data){
    //對異步結果data進行操做
});
promise.then(function(data){
    //對異步結果data進行操做
});
//promise是同一個對象

(3) 能夠鏈式調用this

//例如:
new Promise(function(resolve, reject){
    //異步操做 而且在回調中調用了 resolve或者 reject將異步處理結果傳遞出去
}).then(function(data){}).then(function(data){});

這裏第一次和第二次調用then的對象是兩個不一樣的對象,這裏要注意prototype

[1] 若是第一個then的參數也是一個Promise那麼在第二個then的中會獲得第一個then的處理結果
[2] 若是第一個then的參數是一個函數而且有返回值,則在第二個then中會接收到這個返回值
[3] 其它狀況下都會獲得經過 new Promise(fn) 這裏這個異步fn的處理結果

(4) 咱們寫的Promise 要和全部符合Promise規範的 Promise 能協同工做(例如瀏覽器自帶的、第三方庫的)code

/**
*@parms onResolved 任務成功時執行的回調
*@parms onRejected 任務失敗時執行的回調
*/
Promise.prototype.then = function(onResolved, onRejected) {
    var self = this;
    var promiseThen;  //then方法返回的promise

    //若是 onResolved或onRejected 不是函數咱們忽略它,而且添加一個用於傳遞異步結果得函數
    onResolved = typeof onResolved ==='function' ? onResolved : function(value) {resolve(value);}
    onRejected = typeof onRejected ==='function' ? onRejected : function(reson) {resolve(reson);}

    /*若是在調用then添加依賴於異步處理結果的時候,異步任務已經執行完了,那麼
    用異步的結果執行then裏面添加的任務*/
    if(self.status === 'RESOLVED') {
        //這裏要將then裏面要添加的任務包裝成一個Promise,返回Promise 是爲了(3)中的鏈式調用
        return promiseThen = new Promise(function(resolve, reject) {
            try {
                //執行then添加的任務
                var x = onResolved(self.data);
                if(x instanceof Promise) {
                    /*若是then添加的任務有返回值,而且返回值是Promise對象
                    則讓promiseThen去接受x的狀態和data值注意,這裏將
                    promiseThen的resolve,和reject做爲x的then參數傳入,這
                    樣當promiseThen的resolve和reject會在適當的時候被調用
                    從而使得promiseThen接受了x的狀態和data值Promise的不
                    同對象之間能實現相互影響,是經過將本身的resolve和
                    reject添加到其它對象中*/
                    x.then(resolve, reject); 
                }
                else resolve(x); //若是x不是Promise使用x的值做爲promiseThen的結果
            } catch (e) {
                reject(e);
            }
        });
    }

    //和上面邏輯相同
    if(self.status === 'REJECTED') {
        return promiseThen = new Promise(function(resolve, reject){
            try {
                var x = onRejected(self.data);
                if(x instanceof Promise){
                    x.then(resolve, reject);
                }
            }catch(e) {
                reject(e);
            }
        });
    }

    if(self.status === 'PENDING') {
        //當前Promise的異步任務沒有執行完,則將then裏面的異步任務 包裝後放入隊列
        //包裝方法和上面同樣,區別是上面當即執行,這裏放入回調隊列。
        return promiseThne = new Promise(function() {
            self.onResolvedCallback.push(function(){
                try {
                    var x = onResolved(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }
            });

            self.onRejectedCallback.push(function(){
                try {
                    var x = onRejected(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                } 
            });
        });
    }

}

寫到這裏Promise具體實現的 (1)、(2)、(3)的功能已經完成了,也就是若是不考慮與其它
的Promise交互那麼Promise的原理已經說清楚了,原本打算一篇文章寫完,可是篇幅太
長,因此打算分兩篇文章來寫,下一篇是generator,以後我會按照規範將(4)部分實現。server

相關文章
相關標籤/搜索