Jquery延迟:使用带有promise数组的过滤器

时间:2013-01-30 21:26:58

标签: jquery jquery-deferred

我是一个客户端新手试图围绕jQuery Deferred对象,特别是链接。我有一个promise对象在其过滤工作中替换另一个promise对象的情况:

// works great. output: 
//  about to resolve top layer
//  top layer then results: {"id":"top_layer_deferred"}
//  about to resolve second layer item 0
//  top layer done: {"id":"second_layer_deferred"}
var top_layer_deferred = $.Deferred();

setTimeout(function() {
    console.log('about to resolve top layer');
    top_layer_deferred.resolve( { id: 'top_layer_deferred' } )
}, 10000 );

var top_layer_filter = top_layer_deferred.promise().then( function( results ) {

    console.log('top layer then results: ' + JSON.stringify(results) );
    var second_layer_deferred = $.Deferred();

    setTimeout(function() {
        console.log('about to resolve second layer item 0');
        second_layer_deferred.resolve( { id: 'second_layer_deferred' } )
    }, 2000 );

    return second_layer_deferred.promise();         
});


top_layer_filter.done( function(results) {
    console.log('top layer done: ' + JSON.stringify(results) );
});

现在我想做同样的事情,但在过滤器代码中返回一个promise对象数组。 (为了简单起见,我只是在数组上放置一个promise对象而不使用任何参数来解析。)但是过滤器代码过早地触发,好像它没有看到它的参数:

// doesn't work. output:
//   about to resolve top layer
//   top layer then results: {"id":"top_layer_deferred"}
//   top layer done: 
//   about to resolve second layer item 0
var arr = [];
var top_layer_deferred = $.Deferred();

setTimeout(function() {
    console.log('about to resolve top layer');
    top_layer_deferred.resolve( { id: 'top_layer_deferred' } )
}, 10000 );

var top_layer_filter = top_layer_deferred.promise().then(function( results ) {

    console.log('top layer then results: ' + JSON.stringify(results) );
    var second_layer_deferred = $.Deferred();

    setTimeout(function() {
        console.log('about to resolve second layer item 0');
        second_layer_deferred.resolve()
    }, 2000 );

    arr.push( second_layer_deferred.promise() );
    return arr;             
});

top_layer_filter.done( function() {
    console.log('top layer done: ' );
});

我试过更换线

top_layer_filter.done( function() {

$.when.apply(null,top_layer_filter).done( function() {

但这不会改变结果。

关于我缺少什么的任何想法?

波利

1 个答案:

答案 0 :(得分:1)

好的,我的解释是“一系列承诺是一个数组,而不是一个承诺”显然没有得到重点。这是一个更完整的解释。

Deferred.then()的jQuery 1.8+文档说明了doneFilterfailFilterprogressFilter个参数:

  

这些过滤器函数可以返回要传递的新值   承诺的.done().fail()回调,或者他们可以返回   另一个可以通过它的可观察对象(Deferred,Promise等)   解析/拒绝状态和承诺回调的值。

因此,在jQuery 1.8+中,.then()的行为(尤其是链接到它的任何内容)由返回的内容决定。返回一个可观察对象(Deferred或Promise)与返回任何其他类型的对象根本不同。

您的两个代码示例在这方面有所不同:

  • 在第一个中,.then(function(){...})返回一个Promise,因此传递给链的可观察对象是Promise。
  • 在第二个中,.then(function(){...})返回一个数组,因此向下传递的可观察对象是一个Promise,其状态与从左边提供.then()的状态相同(即“已解决” “),但具有数组的已解析值。

如果你能掌握上面的第二点,那么你应该很好地了解你为什么会得到你所观察到的行为。

要解决此问题,第二个代码示例需要遵循与第一个相同的整体模式。第return second_layer_deferred.promise();行至关重要。 arr可以$.when()完全定义,填充并提交给top_layer_deferred.then(function(){...})

var top_layer_deferred = $.Deferred();
setTimeout(function() {
    console.log('about to resolve top layer');
    top_layer_deferred.resolve('top_layer_deferred');
}, 5000);
var top_layer_filter = top_layer_deferred.promise().then(function(results) {
    console.log('top layer then results: ' + results);
    var second_layer_deferred = $.Deferred();
    var arr = [];
    for(var i=0; i<5; i++) {
        arr[i] = $.Deferred();
        setTimeout(function(ii) {//Note: double-wrap to form closure, ensuring correct i is reported.
            return function() {
                if(ii == 99) {//Edit here: try if(ii == 3)
                    console.log('about to reject second layer item ' + ii);
                    arr[ii].reject('second layer: ' + ii + ' rejected');
                }
                else {
                    console.log('about to resolve second layer item ' + ii);
                    arr[ii].resolve('second layer: ' + ii + ' resolved');
                }
            };
        }(i), 2000 + i * 2000);
        arr[i].done(function(r) {
            console.log('second layer : ' + r);
        }).fail(function(r) {
            console.log('second layer : ' + r);
        });
    }
    //At this point, arr is fully loaded with all the Deferreds it'll ever get,
    //so it's safe to apply $.when().
    $.when.apply(null, arr).done(function() {
        second_layer_deferred.resolve();
    }).fail(function(){
        second_layer_deferred.reject();
    });
    return second_layer_deferred.promise();
});
top_layer_filter.done(function() {
    console.log('top layer done');
}).fail(function() {
    console.log('top layer failed');
});

为了更好的衡量,我已经包含了一种机制,可以使第二级Deferred之一失败(编辑指示的代码)。你会看到

  • 当所有第二级延期成功时,当所有这些延期已经解决时,会报告“顶层已完成”。
  • 当任何第二级延期失败时,会立即报告“顶层失败”。