先來看使用案例:promise
var def1 = $.Deferred(); var def2 = $.Deferred(); var def3 = $.Deferred(); var def4 = $.Deferred(); var fun1 = function (def) { setTimeout(function () { console.log("fun1 resolve"); def.resolve(); }, 3000); return def; }; var fun2 = function (def) { setTimeout(function () { console.log("fun2 resolve"); def.resolve(); }, 2000); return def; }; var fun3 = function (def) { setTimeout(function () { console.log("fun3 resolve"); def.resolve(); }, 1000); return def; }; var fun4 = function (def) { setTimeout(function () { console.log("fun4 resolve"); def.resolve(); }, 1000); return def; }; $.when(fun1(def1), fun2(def2), fun3(def3),fun4(def4)).done(function () { console.log("並行執行完畢"); }); //執行結果: //fun3 resolve //fun4 resolve //fun2 resolve //fun1 resolve //並行執行完畢
執行的過程以下:閉包
源碼分析:函數
// Deferred helper when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. //20170620 huanhua 這就是 when裏面的參數 subordinate必須是返回 deferred對象的函數的緣由。 deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function (value) { contexts[ i ] = this; values[i] = arguments.length > 1 ? core_slice.call(arguments) : value; //20170624 huanhua 在 progess 中時 ,傳遞的 values 就是 progressValues ,因此 此時 values === progressValues 是成立的,觸發 deferred.notifyWith if( values === progressValues ) { deferred.notifyWith(contexts, values); //20170624 huanhua 在 done 時,傳遞的 values 就是 resolveValues ,因此 此時 values === progressValues 是不成立的, //在 remaining = 0,所有都執行完了 ,觸發 deferred.resolveWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array(length); for (; i < length; i++) { //20170624 huanhua 判斷傳入的個參數是不是 deferred 對象 //若是是 if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) { //20170625 huanhua 給 when()參數中的各個對象註冊方法 resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress(updateFunc(i, progressContexts, progressValues)); //若是不是 } else { --remaining; } } } // if we're not waiting on anything, resolve the master //20170624 huanhua 若是一個都沒參數都沒傳遞,就直接執行 if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); }
$.when(fun1(def1), fun2(def2), fun3(def3),fun4(def4))返回的就是一個 Deferred.promise對象.源碼分析
updateFunc = function( i, contexts, values ) { return function (value) { contexts[ i ] = this; values[i] = arguments.length > 1 ? core_slice.call(arguments) : value; //20170624 huanhua 在 progess 中時 ,傳遞的 values 就是 progressValues ,因此 此時 values === progressValues 是成立的,觸發 deferred.notifyWith if( values === progressValues ) { deferred.notifyWith(contexts, values); //20170624 huanhua 在 done 時,傳遞的 values 就是 resolveValues ,因此 此時 values === progressValues 是不成立的, //在 remaining = 0,所有都執行完了 ,觸發 deferred.resolveWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; },
$.when(fun1(def1), fun2(def2), fun3(def3),fun4(def4))中,fun1(def1)給這個的返回結果def1註冊 done(),而且此時給def1.done()註冊的方法是在 done中最後執行,有一段核心代碼this
//在 remaining = 0,所有都執行完了 ,觸發 deferred.resolveWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); }
remaining閉包when的參數個數,當全部的參數都執行完了的時候,就調用 spa
$.when(fun1(def1), fun2(def2), fun3(def3),fun4(def4))中的對象 deferred.resolveWith().code
經過這個思路咱們能夠簡化個案例,獲得這個全部人都要度過河的案例:對象
var Person = function (name) { var name = name; var listDo = []; this.do = function (fn) { listDo.push(fn); }; this.fire = function () { for (var xh in listDo) { listDo[xh](name); } }; }; var Duhe = function (person1, person2, person3, person4) { var listDo = []; var length = arguments.length; var that = this; var interval = [3000,2000,2500,1000]; for (var arg in arguments) { arguments[arg].do(function (name) { setTimeout(function () { console.log(name + ":渡河成功!"); if (!(--length)) { that.fire(); } }, interval[arg]); }); }; for (var arg in arguments) { arguments[arg].fire(); }; this.do = function (fn) { listDo.push(fn); return this; }; this.fire = function () { for(var xh in listDo){ listDo[xh](); } }; } var duhe = new Duhe(new Person("Person1"), new Person("Person2"), new Person("Person3"), new Person("Person4"), new Person("Person5")); duhe.do(function () { console.log("全部人員都渡河成功!"); }); //答案: //Person5:渡河成功! //Person4:渡河成功! //Person2:渡河成功! //Person3:渡河成功! //Person1:渡河成功! //全部人員都渡河成功!
在咱們實際工做中這個思路很重要。blog