So I was asked this at an interview, but it brought up a good use case. Assume that you have a bunch of data sources. You want to find the first available one and process it and ignore the rest.
所以我在接受采访时被问到这个问题,但它提出了一个很好的用例。假设您有一堆数据源。你想找到第一个可用的并处理它而忽略其余的。
So something like:
所以类似于:
var datasources = new Array("somedatabase1/pizza","somedatabase2/beer","somedatabase3/llama");
var dfds = new Array();
$.each(datasources,function(source){
dfds.push($.getJSON(source));
});
$.when(dfds).done(function(){alert("they are all done");});
Ignore that I really don't think when accepts an array (maybe it does). This of course would make it wait till they are all completed. I am looking for some code that would make it wait until one, any of them is done, and then not worry about the others.
忽略我真的不认为什么时候接受一个数组(也许它)。这当然会让它等到它们全部完成。我正在寻找一些代码,让它等到一个,其中任何一个完成,然后不用担心其他的。
I was informed that it would only work recursively.
我被告知它只能递归地工作。
2 个解决方案
#1
3
This doesn't use recursion but fits the requirement to fetch from multiple datasources and only care about the first that returns a successful response.
这不使用递归,但符合从多个数据源获取的要求,只关心返回成功响应的第一个。
http://jsfiddle.net/mNJ6D/
function raceToIt(urls) {
var deferred = $.Deferred(),
promises;
function anyComplete(data) {
if (!deferred.isResolved()) {
deferred.resolveWith(this, [data]);
promises.forEach(function(promise) {
promise.abort();
});
}
}
promises = urls.map(function(url) {
return $.getJSON(url).then(anyComplete);
});
return deferred.promise();
}
raceToIt(["/echo/json/", "/echo/json/", "/echo/json/"]).then(function(data) {
console.log(data);
});
#2
1
I've made a plugin which provides another version of $.when()
with reversed semantics. It's modified from the actual jQuery implementation of $.when()
so it's exactly the same as the original except that it waits for either the first resolve
d promise, or all promised to be reject
ed.
我已经制作了一个插件,它提供了另一个带有反向语义的$ .when()版本。它是从$ .when()的实际jQuery实现中修改的,所以它与原始的完全相同,只是它等待第一个已解决的promise,或者所有承诺被拒绝。
Just drop this code in right after you load jQuery:
只需在加载jQuery后立即删除此代码:
(function($) {
$.reverseWhen = function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
rejectValues = Array.prototype.slice.call( arguments ),
length = rejectValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If rejectValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both reject and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? Array.prototype.slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.rejectWith( contexts, values );
}
};
},
progressValues, progressContexts, rejectContexts;
// add listeners to Deferred subordinates; treat others as rejected
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
rejectContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( rejectValues[ i ] && jQuery.isFunction( rejectValues[ i ].promise ) ) {
rejectValues[ i ].promise()
.done( deferred.resolve )
.fail( updateFunc( i, rejectContexts, rejectValues ) )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, reject the master
if ( !remaining ) {
deferred.rejectWith( rejectContexts, rejectValues );
}
return deferred.promise();
};
})(jQuery);
#1
3
This doesn't use recursion but fits the requirement to fetch from multiple datasources and only care about the first that returns a successful response.
这不使用递归,但符合从多个数据源获取的要求,只关心返回成功响应的第一个。
http://jsfiddle.net/mNJ6D/
function raceToIt(urls) {
var deferred = $.Deferred(),
promises;
function anyComplete(data) {
if (!deferred.isResolved()) {
deferred.resolveWith(this, [data]);
promises.forEach(function(promise) {
promise.abort();
});
}
}
promises = urls.map(function(url) {
return $.getJSON(url).then(anyComplete);
});
return deferred.promise();
}
raceToIt(["/echo/json/", "/echo/json/", "/echo/json/"]).then(function(data) {
console.log(data);
});
#2
1
I've made a plugin which provides another version of $.when()
with reversed semantics. It's modified from the actual jQuery implementation of $.when()
so it's exactly the same as the original except that it waits for either the first resolve
d promise, or all promised to be reject
ed.
我已经制作了一个插件,它提供了另一个带有反向语义的$ .when()版本。它是从$ .when()的实际jQuery实现中修改的,所以它与原始的完全相同,只是它等待第一个已解决的promise,或者所有承诺被拒绝。
Just drop this code in right after you load jQuery:
只需在加载jQuery后立即删除此代码:
(function($) {
$.reverseWhen = function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
rejectValues = Array.prototype.slice.call( arguments ),
length = rejectValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If rejectValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both reject and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? Array.prototype.slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.rejectWith( contexts, values );
}
};
},
progressValues, progressContexts, rejectContexts;
// add listeners to Deferred subordinates; treat others as rejected
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
rejectContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( rejectValues[ i ] && jQuery.isFunction( rejectValues[ i ].promise ) ) {
rejectValues[ i ].promise()
.done( deferred.resolve )
.fail( updateFunc( i, rejectContexts, rejectValues ) )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, reject the master
if ( !remaining ) {
deferred.rejectWith( rejectContexts, rejectValues );
}
return deferred.promise();
};
})(jQuery);