0

私はクライアント側の初心者で、jQuery Deferred オブジェクト、特にチェーンに頭を悩ませようとしています。フィルタリングが機能しているときに、あるプロミス オブジェクトが別のプロミス オブジェクトに置き換わるケースがあります。

// 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 オブジェクトを 1 つだけ配置し、解決する引数を使用していません。) しかし、フィルター コードは、あたかもその引数が表示されないかのように、時期尚早に起動します。

// 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

OK、「promise の配列は配列であり、promise ではない」という私の説明は、明らかに要点を理解していませんでした。ここに、より完全な説明があります。

jQuery 1.8+ のドキュメントにDeferred.then()は、そのdoneFilter,failFilterおよびprogressFilter引数について次のように記載されています。

.done()これらのフィルター関数は、Promiseまたはコールバックに渡される新しい値を.fail()返すか、別の監視可能なオブジェクト (Deferred、Promise など) を返し、解決済み/拒否されたステータスと値を Promise のコールバックに渡します。

そのため、jQuery 1.8+ では、.then()(より具体的にはそれにチェーンされているものの) 動作は、何が返されるかによって決定されます。監視可能なオブジェクト (Deferred または Promise) を返すことは、他のタイプのオブジェクトを返すこととは根本的に異なります。

この点で 2 つのコード サンプルが異なります。

  • 最初.then(function(){...})は Promise を返すため、チェーンを下って渡される監視可能なオブジェクトはその Promise です。
  • 2 番目.then(function(){...})は配列を返すため、チェーンを下って渡されるオブザーバブル オブジェクトは Promise であり、そのステータスは.then()左から供給されるものと同じ (つまり、「解決済み」) ですが、配列の解決された値を持ちます。

上記の 2 番目のポイントを把握できれば、観察した行動がなぜ起こったのかを理解するための道を順調に進んでいるはずです。

この問題を解決するには、2 番目のコード サンプルが最初のコード サンプルと同じ全体的なパターンに従う必要があります。ライン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');
});

適切な措置として、第 2 レベルの Deferred の 1 つを失敗させるメカニズムを含めました (示されているコードを編集します)。あなたはそれを見るでしょう

  • すべての第 2 レベルの Deferred が成功すると、それらすべての Deferred が解決されると「top layer done」が報告されます。
  • 第 2 レベルの Deferred のいずれかが失敗すると、「最上位層の失敗」がすぐに報告されます。
于 2013-01-31T23:06:33.753 に答える