【问题标题】:$.when.apply on Array of Promises$.when.apply 用于 Promise 数组
【发布时间】:2014-02-26 05:56:27
【问题描述】:

我搜索了许多非常相似的问题。我发现一个几乎完全符合我的要求,只是它没有任何错误处理,并且在尝试实施错误处理时我遇到了一些问题。

我的目标是创建一个接收名称数组的函数,在循环中创建 $.ajax 请求,将返回的承诺对象推送到一个数组中,然后我将其提供给 $.when.apply($,arrayOfPromises).then (// 等来解决一个 master deferred,其中 arrayOfPromises 中的所有承诺都已解决。最终结果是创建一个从 ajax 调用返回的数据数组。

到目前为止我有这个:http://jsfiddle.net/BGx2h/1/

这几乎正是我所需要的。如果数组包含指向所有有效资源的指针,那么一切似乎都按预期工作。但是,如果调用不存在的资源(例如我的小提琴中的 /test/doesnt.exist.txt),则对“doesnt.exist”的 ajax 调用在任何其他调用之前完成,主deferred (connectiondfd) 在其他调用有机会完成之前得到解决(您可能需要多次运行小提琴才能获得此结果)。这是我的函数目前的样子:

function multiAsync() {
    var i,data=[],connections=[],
        targets=['John.Smith','Jane.Doe','Bob.Someone','doesnt.exist'];

    var connectiondfd = $.Deferred();

    for(i=0;i<targets.length;i++) {
        connections.push($.ajax({
            url:'http://porticium.ca/test/'+targets[i]+'.txt',
            type:'GET',
            async:true,
            timeout:5000
        }).then(function(newData) {
            data.push(newData);
        },function() {
            data.push("NO DATA");
        }));
}

    $.when.apply($,connections)
        .then(
            function() { connectiondfd.resolve(); },
            function() { connectiondfd.resolve(); }
        );


    $.when(connectiondfd).done(function() {
        alert("FINAL: " + data);
    });
}

非常感谢您对此的帮助,这让我发疯了!

谢谢, 抢

【问题讨论】:

  • 使用错误处理,当其中一个承诺失败时,您将获得失败处理程序并且永远不会成功。由于您失败的承诺几乎立即完成,它总是会在其余完成之前显示失败。如果这不是您想要的功能,您可能需要考虑使用延迟对象以外的其他对象,例如 $.Callbacks 对象。
  • 感谢您的快速回复。我正在研究 $.Callbacks 现在。所以基本上没有办法说 $.when.apply($,listOfPromises).whenALLPromisesHaveResolvedOrRejected(function() {});以这种方式,我不只是错过了什么吗?
  • 正确。一旦失败,等待就完成了。
  • 我明白了,非常感谢。 $.Callbacks 看起来正是我所需要的。

标签: javascript jquery ajax promise deferred


【解决方案1】:

所以我更多地使用它并研究我接受的答案中提供的功能(再次感谢!),我设法找到了另一个使用适合我目的的延迟的解决方案。

我创建了一个名为 whenAll 的函数,它接受一个 promise 数组,创建一个 master deferred,并向每个 promise 添加完成和失败回调,将返回的数据或错误消息推送到数组中,然后检查长度数据数组的大小与提供的 promises 数组的长度,当它们相同时解析。

这里是:

function whenAll(promises) {
    var i,data=[],dfd=$.Deferred();
    for(i=0;i<promises.length;i++) {
        promises[i].done(function(newData) {
            data.push(newData);
            if(data.length==promises.length) {
                dfd.resolve(data);
            }
        }).fail(function() {
            data.push("NO DATA");
            if(data.length==promises.length) {
                dfd.resolve(data);
            }            
        });
    }
    return dfd.promise();
}

【讨论】:

    【解决方案2】:

    虽然$.Deferred.when 函数会在你的一个promises 失败时立即触发,但你可以创建一个'wrapper' Deferred 来处理一组promises 并在它们进入时调度它们,然后在所有完成后触发master 自己的处理程序,即使有些失败,有些成功。这个特殊功能只是从我拥有的通用工具文件中复制而来,为了简洁起见,使用Underscore,但基本模式是您所需要的:

        function completed( firstParam ) {
    
            var args = _.toArray( arguments ),
                i = 0,
                length = args.length,
                pValues = new Array( length ),
                count = length,
                deferred = length <= 1 && firstParam && $.isFunction( firstParam.promise ) ? firstParam : $.Deferred(),
                promise = deferred.promise(),
                state = 'resolved';
    
            function alwaysFunc( i ) {
    
                return function ( value ) {
    
                    args[ i ] = arguments.length > 1 ? _.toArray( arguments ) : value;
                    state = ( this.state && this.state() === 'rejected' ) ? 'rejected' : state;
                    if ( !( --count ) ) deferred[ ( state === 'rejected' ? 'reject' : 'resolve' ) + 'With' ]( deferred, args );
    
                };
    
            }
    
            function progressFunc( i ) {
    
                return function ( value ) {
    
                    pValues[ i ] = arguments.length > 1 ? _.toArray( arguments ) : value;
                    deferred.notifyWith( promise, pValues );
    
                };
    
            }
    
            if ( length > 1 ) {
    
                for ( ; i < length; i++ ) {
    
                    if ( args[ i ] && args[ i ].promise && $.isFunction( args[ i ].promise ) )
                        args[ i ].promise().always( alwaysFunc( i ) ).progress( progressFunc( i ) );
    
                    else --count;
    
                }
    
                if ( !count ) deferred.resolveWith( deferred, args );
    
            } else if ( deferred !== firstParam ) deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
    
            return promise;
    
        }
    

    无论如何,您可以像往常一样创建处理程序并将其附加到单个请求/承诺,然后通过apply 将它们全部传递给此函数。每个请求的状态都是单独处理的,并且该函数会跟踪仍有多少未解决。全部解决后,它会根据集合触发自己的解决方案。即使一个或所有组件 Promises 失败,所有组件仍会执行,并且 master Deferred 会等待它们全部解决。它不处理在初始调用后添加进一步的承诺/延迟 - 创建所有你需要的,然后将它们传递给这个函数。

    我不能把脚本归功于这个脚本:它是通过一位同事“传给我”的——我认为他是从其他地方得到的,但在代码中保留 cmets/attributions 是世界上最糟糕的人。如果有人认出代码并可以指出作者的方向......

    【讨论】:

    • 这就像我需要的魔法一样。我不能说我了解所有这些,但我绝对知道接下来的一段时间我将学习什么。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2020-10-27
    • 1970-01-01
    • 2022-12-16
    • 2018-08-17
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    • 2017-01-30
    相关资源
    最近更新 更多