jquery中的 deferred之 when (三)
先来看使用案例:
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中最后执行,有一段核心代码
//在 remaining = 0,全部都执行完了 ,触发 deferred.resolveWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); }
remaining闭包when的参数个数,当所有的参数都执行完了的时候,就调用
$.when(fun1(def1), fun2(def2), fun3(def3),fun4(def4))中的对象 deferred.resolveWith().
通过这个思路我们可以简化个案例,得到这个所有人都要度过河的案例:
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:渡河成功! //所有人员都渡河成功!
在我们实际工作中这个思路很重要。