promise異步編程的原理

一.起源

JavaScript中的異步由來已久,不管是定時函數,事件處理函數仍是ajax異步加載都是異步編程的一種形式,咱們如今以nodejs中異步讀取文件爲例來編寫一個傳統意義的異步函數:node

var fs = require('fs'); function readJSON(filename,callback){ fs.readFile(filename,'utf8',function(err,res){ if(err){ return callback(err,null); } try{ var res = JSON.parse(res); }catch(ex){ callback(ex) } callback(null,res); }); }

若是咱們想異步讀取一個json文件,它接受2個參數,一個文件名,一個回調函數。文件名必不可少,關鍵就在這個callback上面了,當咱們要執行這個readJSON函數時,要本身構造想要的回調函數,可是在readJSON函數內部傳遞callback時候不知道他的參數,顯然是不友好的。下面在看一種異步編程的代碼:jquery

fs.readFile('file1.txt','utf8',function(err,res){ fs.readFile('file2.txt','utf8',function(err,res){ fs.readFile('file2.txt','utf8',function(err,res){ console.log(res); }); }); });

這裏嵌套了3個異步回調函數,他們的執行時刻都是不可預測的而且這樣寫代碼也不符合普通程序的執行流程。ajax

因此,問題來了,promise提供了一個解決上述問題的模式。編程

二.定義

promise是對異步編程的一種抽象。它是一個代理對象,表明一個必須進行異步處理的函數返回的值或拋出的異常。也就是說promise對象表明了一個異步操做,能夠將異步對象和回調函數脫離開來,經過then方法在這個異步操做上面綁定回調函數。json

下面介紹具體API,這裏遵循的是commonJS promise/A+規範。promise

1.狀態

promise有3種狀態:pending(待解決,這也是初始化狀態),fulfilled(完成),rejected(拒絕)。框架

2.接口

promise惟一接口then方法,它須要2個參數,分別是resolveHandler和rejectedHandler。而且返回一個promise對象來支持鏈式調用。dom

promise的構造函數接受一個函數參數,參數形式是固定的異步任務,舉一個栗子:異步

function sendXHR(resolve, reject){ var xhr = new XMLHttpRequest(); xhr.open('get', 'QueryUser', true); xhr.onload = function(){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){ resolve(xhr.responseText); }else{ reject(new Error(xhr.statusText)); } }; xhr.onerror = function(){ reject(new Error(xhr.statusText)); } xhr.send(null) }

三.實現

 要實現promise對象,首先要考慮幾個問題:async

1.promise構造函數中要實現異步對象狀態和回調函數的剝離,而且分離以後可以還能使回調函數正常執行

2.如何實現鏈式調用而且管理狀態

首先是構造函數:

//全局宏定義
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
//Promise構造函數
function Promise(fn){
    var self = this;
    self.state = PENDING;//初始化狀態
    self.value = null;//存儲異步結果的對象變量
    self.handlers = [];//存儲回調函數,這裏沒保存失敗回調函數,由於這是一個dome
    //異步任務成功後處理,這不是回調函數
    function fulfill(result){
        if(self.state === PENDING){
            self.state = FULFILLED;
self.value = result; for(var i=0;i<self.handlers.length;i++){ self.handlers[i](result); } } } //異步任務失敗後的處理, function reject(err){ if(self.state === PENDING){ self.state = REJECTED; self.value = err; } } fn&&fn(fulfill,reject); };

 構造函數接受一個異步函數,而且執行這個異步函數,修改promise對象的狀態和結果。

回調函數方法then:

//使用then方法添加回調函數,把此次回調函數return的結果當作return的promise的resolve的參數
Promise.prototype.then = function(onResolved, onRejected){
    var self = this;
    return new Promise(function(resolve, reject){
        var onResolvedFade = function(val){
            var ret = onResolved?onResolved(val):val;//這一步主要是then方法中傳入的成功回調函數經過return來進行鏈式傳遞結果參數
            if(Promise.isPromise(ret)){//回調函數返回值也是promise的時候
                ret.then(function(val){
                    resolve(val);
                });
            }
            else{
                resolve(ret);
            }
        };
        var onRejectedFade = function(val){
            var ret = onRejected?onRejected(val):val;
            reject(ret);
        };
        self.handlers.push(onResolvedFade);
        if(self._status === FULFILLED){
            onResolvedFade(self._value);
        }

        if(self._status === REJECTED){
            onRejectedFade(self._value);
        }
    });
}

經過上面的代碼能夠看出,前面提出的2個問題獲得瞭解決,1.在promise對象中有3個屬性,state,value,handlers,這3個屬性解決了狀態和回調的脫離,而且在調用then方法的時候纔將回調函數push到handlers屬性上面(此時state就是1,能夠在後面的代碼中執行onResolve)2.鏈式調用經過在then方法中返回的promise對象實現,而且經過onResolvedFade將上一個回調的返回值當作此次的result參數來執行進行傳遞。

測試代碼:

function async(value){
    var pms = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(value);;
        }, 1000);
    });
    return pms;
}
async(1).then(function(result){
    console.log('the result is ',result);//the result is 2 
    return result;
}).then(function(result){
    console.log(++result);//2
});

 

總之,不一樣框架對promise的實現大同小異,上面的代碼是ECMASCRIPT6標準的promise簡單實現。jquery和其餘框架也有實現,不過據說jquery的實現很糟糕- -!

相關文章
相關標籤/搜索