jquery中的 deferred之 when (三)

时间:2021-03-15 19:09:57

先来看使用案例:

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
//并行执行完毕

执行的过程如下:

jquery中的 deferred之 when (三)

源码分析:

// 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:渡河成功!
//所有人员都渡河成功!

在我们实际工作中这个思路很重要。