// Deferred helper (3132)
when 是延迟对象 Deferred的一个辅助方法。
var dfd = $.Deferred(); //创建延迟对象
dfd。done();
dfd.fail();
//使用:
$.when().done();
$.when().fail();
when的返回值,是一个延迟对象。
源码: return deferred.promise();
举个例子:when可以等待多个延迟对象都成功后,触发成功。
例子:(1)
成功必须多个都成功。
function aaa() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('aaa');
dfd.resolve();
}, 1000);
return dfd;
}
function bbb() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('bbb');
dfd.resolve();
}, 2000);
return dfd;
}
$.when(aaa(), bbb()).done(function () {
alert('done');
});
// 目标是aaa和bbb都完成以后,才触发这个成功。
// 只有一个延迟对象成功,不会判定是否成功。
// 这里就是等aaa 和 bbb都完成以后。才执行done函数。
例子:(2)
失败触发,只要一个失败就可以。而且失败的时候,就立即触发回调函数。
function aaa() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('aaa');
dfd.reject();
}, 1000);
return dfd;
}
function bbb() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('bbb');
dfd.resolve();
}, 2000);
return dfd;
}
$.when(aaa(), bbb()).fail(function () {
alert('fail');
});
// 目标是aaa和bbb有一个失败,就立刻出发失败这个函数。
这里的argus是参数,很多个延迟对象的代表的状态。
$.when(argus).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
argus[i] 对应着每一个延迟对象,jQuery有计数器。来计算所有argus的状态。
只要满足了条件,就可以触发说对应的事件。
比如传入三个参数:
$.when(A, B, C).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
那么代码中就会产生一个计数器。来判断这个三个延迟对象是否已经完成,
如果完成一个,就-1,当计数器为0的时候,触发成功。
当然,如果有一个延迟对象失败了,就直接触发失败的回调函数。
例子(3)
function aaa() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('aaa');
// dfd.reject();
}, 1000);
return dfd;
}
function bbb() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('bbb');
dfd.resolve();
}, 2000);
return dfd;
}
$.when(aaa(), bbb()).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
注意:执行结果是 打印 aaa bbb ,但是不会触发事件,因为aaa()对象中,一直没有触发延迟对象的状态改变。
所以,when就只能等待。
例子(4)
function aaa() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('aaa');
// dfd.reject();
}, 1000);
return dfd;
}
function bbb() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('bbb');
dfd.resolve();
}, 2000);
return dfd;
}
$.when(aaa(), bbb()).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
测试结果是:打印 aaa bbb, 然后弹出done。
为什么呢?状态都完成了,但是aaa没有返回。导致,aaa()中的reject,就没有起作用。
因为在, $.when() 中传入的是延迟对象,但是函数没有返回值,也就没有延迟对象的传入,
因此,aaa(), 这个参数,就没办法起到作用了。就跳过去了。因为bbb(),完成了,就导致触发成功。
例子(5)
A:状态
$.when().done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
B:状态
$.when(123, 1234).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
A状态和B状态都是直接触发成功的。
当$.when(argus)中的参数argus不是延迟对象,就直接跳过,就相当于没写。
例子(6)
function aaa() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('aaa');
dfd.reject();
}, 1000);
// return dfd;
}
function bbb() {
var dfd = $.Deferred(); //创建延迟对象
setTimeout(function () {
console.log('bbb');
dfd.resolve();
}, 2000);
return dfd;
}
$.when(aaa(), bbb(), 123).done(function() {
alert('done');
alert(arguments[2]);
}).fail(function () {
alert('fail');
alert(arguments[2]);
});
//这里的 arguments[0]是123, arguments[1]是1234.
$.when(123, 1234).done(function() {
alert('done');
}).fail(function () {
alert('fail');
});
例子(7)
$.when(aaa(), 123, bbb(). 345).done(function() {
alert('done');
alert(arguments[1]);
alert(arguments[3]);
}).fail(function () {
alert('fail');
alert(arguments[1]);
alert(arguments[3]);
});
这里传入了4个参数,但是只有2个是延迟对象, 因此,需要判断并更新,--remaining操作,将不是延迟对象的去掉。
总结一下思想:首先判读传入的参数,有几个延迟对象,如果一个没有,就直接执行done。自执行done,fail是不会执行的。
如果有,就检测执行,判断计数器是否为0了,并且是成功的返回(resolve),为0了就执行对应的成功函数,
如果有一个是失败的(reject),OK,那就是直接调用fail就好了。
when 源码:
// Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
//将arguments---》》》转换为数组。
resolveValues = core_slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
// (1) 这个就是计数器,判断是否还有未完成的情况
// (2) 如果是传入的是空的, 返回0,如果是正常的延迟对象,就返回length。
// jQuery.isFunction( subordinate.promise ), 判断传入的是不是一个延迟对象。
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
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;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) { // 这里减到0,就触发。
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++ ) {
// 判断一下是否是延迟对象。
// resolveValues 这个就是之前处理传入参数,返回的数组。
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) ) //这里还需要判断,计数器是否 到了0.
.fail( deferred.reject ) // 不判断,直接触发了。只要有一个未完成,就直接触发了。
.progress( updateFunc( i, progressContexts, progressValues ) );
}
// 如果不是延迟对象,就--,将计数器变少。
else {
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
// 这里针对不传入参数的状态,给了一个解释。就是直接执行。
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}