遅延コール--deferred.jsコード解析

10533 ワード

次の操作を行うには、前の操作が完了するまで待つ必要があります.たとえばAjaxがフォームの自動コミット操作を実現する場合、プログラムは待つ必要があり、結果が戻ったら、ステップ操作を続けます.
その時jsというライブラリが誕生しました.もちろん、jqueryにもこの機能があります.このライブラリの原理を分析します.
/**
 * @fileOverview JSDeferred
 * @author       [email protected]
 * @version      0.4.0
 * @license
 * JSDeferred Copyright (c) 2007 cho45 ( www.lowreal.net )
 *
 *    deferred       
 */ ; // no warnings for uglify

function Deferred () {
    return this.init();
}
//      
Deferred.ok = function(x) {return x} //       
Deferred.ng = function(x) {throw x}
//     Deferred   
Deferred.isDeferred = function (obj) {
    return !!(obj && obj._id === Deferred.prototype._id);
};

//  next   Deferred      。     .next    
Deferred.next = function(fn){
    var d = new Deferred();
    var img = new Image();
    var handler = function(){
        d.canceller();
        d.calls();
    }
    //            ,    img                。
    //        .next().next()...
    //              ,    , setTimeout....
    img.addEventListener('error',handler,false);
    d.canceller = function(){
        img.removeEventListener('error',handler,false);
    }
    //        img     
    img.src = "data:image/png," + Math.random();
    if(fn) d.callback.ok = fn;
    return d;
}

//                  
//   ,         ,   ajax  
Deferred.wait = function (n) {
    var d = new Deferred(), t = new Date();
    var id = setTimeout(function () {
        d.calls((new Date()).getTime() - t.getTime());
    }, n * 1000);
    d.canceller = function () { clearTimeout(id) };
    return d;
};


Deferred.prototype = {
    _id : 8888, //    ,       Deferred   
    init : function(){
        this._next = null;
        // Deferred.isDeferred     
        this.callback = {
            ok : Deferred.ok,
            ng : Deferred.ng
        }
        return this;
    },
    next  : function (fun) { return this._post("ok", fun) },

    calls  : function (val) { return this._fire("ok", val) },

    _post : function (okng, fun) {
        //    ,       ,
        //._next        ,     
        this._next =  new Deferred();
        this._next.callback[okng] = fun;
        return this._next;
    },

    _fire : function (okng, value) {
        var next = "ok";

        value = this.callback[okng](value);
        //   value    Deferred   
        if (Deferred.isDeferred(value)) {
            //       
            value._next = this._next;
        } else {
            //          
            if (this._next) this._next._fire(next, value);
        }
        return this;
    }
}
 

分析に協力するために、まずテストコードをあげて、その流れを追跡しやすいです.
Deferred.next(function(){
    alert(1)
    return Deferred.wait(3)
}).next(function(){
    alert(2)
})

ここではチェーン式の書き方を使っていて、jqueryをよく知っている人は、これに慣れているに違いありません.まず、Deferedを呼び出します.next()このメソッドは、Deferredのインスタンスを返し、インスタンス上のnext()メソッドを呼び出します.
第1の関数(ポップアップ1)を実行した後、第2の関数(ポップアップ2)を実行することを意図し、書き方が同期しているにもかかわらず、実行するときは非同期である.なぜsettimeoutを直接使わないのかと聞かれるかもしれません.
この問題はとても良くて、実はとても情況の下で、私は確かに等価交換ができると思います.しかしsettimeoutには、前のステップの実行が完了するまでどれくらい待たなければならないかを事前に知らなければならないという使用前提があります.Deferredは必要ありません.
この时、あなたはまた聞くかもしれませんが、どうして返事をしなくてもいいのですか.今度はやっと何も言えなくなった.この書き方を直接説明します.
function(next1,next2,next3,...){
    //....
    //....
    next1(function(){
        next2(function(){
            next3(...);
        })
    })
}

タケノコのように、1枚1枚セット.関数体が少し長ければ、めまいがしませんか?deferredの書き方を見てみましょう.
Deferred.next(function(){
    next1();
}).next(function(){
    next2();
}).next(function(){
    next3()
}).next(function(){
    //...
})

マージャンのように、一字に並べて、おしゃれなチェーンの使い方です.
どのように書くかはどうでもいいと思う場合は、next 1が時間のかかる不確定な操作である場合は、実行順序を保証します.どうやって破るの?
あなたがどんなに破れても、どうせ私はDeferredを選んだので、決めました.