使用jQuery Deferred查找第一个可用的数据源

所以我在接受采访时被问到这个问题,但它提出了一个很好的用例。 假设您有一堆数据源。 你想找到第一个可用的并处理它而忽略其余的。

所以类似于:

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");}); 

忽略我真的不认为什么时候接受一个数组(也许它)。 这当然会让它等到它们全部完成。 我正在寻找一些代码,让它等到一个,其中任何一个完成,然后不用担心其他的。

我被告知它只能递归地工作。

这不使用递归,但符合​​从多个数据源获取的要求,只关心返回成功响应的第一个。

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); });​ 

我已经制作了一个插件,它提供了另一个带有反向语义的$.when()版本。 它是从$.when()的实际jQuery实现中修改的,所以它与原始的完全相同,只是它等待第一个resolve d promise或者所有承诺reject

只需在加载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);