0

我是一个客户端新手,试图围绕 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() {

但这不会改变结果。

关于我所缺少的任何想法?

波莉

4

1 回答 1

1

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

jQuery 1.8+ 文档Deferred.then()谈到了它的doneFilter,failFilterprogressFilterarguments :

这些过滤器函数可以返回一个新值以传递给Promise.done().fail()回调,或者它们可以返回另一个可观察对象(Deferred、Promise 等),它将其已解决/拒绝状态和值传递给 Promise 的回调。

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

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

  • 首先,.then(function(){...})返回一个 Promise,因此沿链传递的可观察对象就是该 Promise。
  • 在第二个中,.then(function(){...})返回一个数组,因此沿链传递的可观察对象是一个 Promise,其状态与.then()从其左侧馈送的状态相同(即“已解决”),但具有 Array 的已解决值。

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

要解决此问题,第二个代码示例需要遵循与第一个相同的整体模式。这条线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');
});

为了更好地衡量,我已经包含了一种机制来导致第二级 Deferreds 失败(在指示的地方编辑代码)。你会看到

  • 当所有第二级 Deferred 都成功时,当所有这些 Deferred 都已解决时,将报告“top layer done”。
  • 当任何第二级延迟失败时,立即报告“顶层失败”。
于 2013-01-31T23:06:33.753 回答