すべての約束が解決するのを待ちます


107

したがって、長さが不明な複数のプロミスチェーンがある状況にあります。すべてのチェーンが処理されたときに何らかのアクションを実行したいのですが。それは可能ですか?次に例を示します。

app.controller('MainCtrl', function($scope, $q, $timeout) {
    var one = $q.defer();
    var two = $q.defer();
    var three = $q.defer();

    var all = $q.all([one.promise, two.promise, three.promise]);
    all.then(allSuccess);

    function success(data) {
        console.log(data);
        return data + "Chained";
    }

    function allSuccess(){
        console.log("ALL PROMISES RESOLVED")
    }

    one.promise.then(success).then(success);
    two.promise.then(success);
    three.promise.then(success).then(success).then(success);

    $timeout(function () {
        one.resolve("one done");
    }, Math.random() * 1000);

    $timeout(function () {
        two.resolve("two done");
    }, Math.random() * 1000);

    $timeout(function () {
        three.resolve("three done");
    }, Math.random() * 1000);
});

この例では、$q.all()ランダムな時間に解決されるpromise 1、2、および3 にを設定します。次に、1と3の最後にプロミスを追加します。allすべてのチェーンが解決されたら、を解決してください。このコードを実行したときの出力は次のとおりです。

one done 
one doneChained
two done
three done
ALL PROMISES RESOLVED
three doneChained
three doneChainedChained 

チェーンが解決するのを待つ方法はありますか?

回答:


161

すべてのチェーンが解決されたら、すべてを解決してほしい。

もちろん、各チェーンのプロミスをall()最初のプロミスの代わりに渡します。

$q.all([one.promise, two.promise, three.promise]).then(function() {
    console.log("ALL INITIAL PROMISES RESOLVED");
});

var onechain   = one.promise.then(success).then(success),
    twochain   = two.promise.then(success),
    threechain = three.promise.then(success).then(success).then(success);

$q.all([onechain, twochain, threechain]).then(function() {
    console.log("ALL PROMISES RESOLVED");
});

2
私の最悪の恐怖を確認してくれてありがとう。今、私は最後の約束笑を取得する方法を考え出す必要があります。
jensengar 14

それの何が問題なのですか?チェーンは動的に構築されていますか?
ベルギ

まさに私の問題です。私は動的にプロミスチェーンを作成しようとしていますが、チェーンが完了したときに何かしたいと思います。
ジェンセンガー、2014

コードを見せてもらえますか?Q.all実行後にチェーンに追加されたアイテムはありますか?それ以外の場合、それは簡単なはずです。
ベルギ

コードをお見せしたいのですが...まだ書き終えていませんが、できる限り説明します。実行する必要がある「アクション」のリストがあります。これらのアクションには、任意の数のレベルのサブアクションが関連付けられている場合があります。すべてのアクションとそのサブアクションが完了したときに何かできるようになりたいです。には複数$q.allのが存在する可能性がありますが、解決プロセスを開始すると、新しいアクションや約束が連鎖することはありません。
jensengar 14

16

受け入れ答えは正しいです。に慣れていない人に少し詳しく説明する例を紹介しpromiseます。

例:

私の例では、コンテンツをレンダリングする前に、可能であればsrcimgタグの属性を別のミラーURL に置き換える必要があります。

var img_tags = content.querySelectorAll('img');

function checkMirrorAvailability(url) {

    // blah blah 

    return promise;
}

function changeSrc(success, y, response) {
    if (success === true) {
        img_tags[y].setAttribute('src', response.mirror_url);
    } 
    else {
        console.log('No mirrors for: ' + img_tags[y].getAttribute('src'));
    }
}

var promise_array = [];

for (var y = 0; y < img_tags.length; y++) {
    var img_src = img_tags[y].getAttribute('src');

    promise_array.push(
        checkMirrorAvailability(img_src)
        .then(

            // a callback function only accept ONE argument. 
            // Here, we use  `.bind` to pass additional arguments to the
            // callback function (changeSrc).

            // successCallback
            changeSrc.bind(null, true, y),
            // errorCallback
            changeSrc.bind(null, false, y)
        )
    );
}

$q.all(promise_array)
.then(
    function() {
        console.log('all promises have returned with either success or failure!');
        render(content);
    }
    // We don't need an errorCallback function here, because above we handled
    // all errors.
);

説明:

AngularJS docsから:

then方法:

then(successCallback、errorCallback、notifyCallback) – promiseが解決または拒否または拒否されるタイミングに関係なく、結果が利用可能になるとすぐに、成功またはエラーのコールバックの1つを非同期的に呼び出します。コールバックは、単一の引数(結果または拒否理由)で呼び出されます。

$ q.all(約束)

複数のプロミスを1つのプロミスに結合し、すべての入力プロミスが解決されると解決されます。

promisesparamが約束の配列にすることができます。

についてbind()、詳細はこちら:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind


thenメソッドに$q.allは返さthenthenたpromiseの配列が用意されているため、promiseをに追加するときに呼び出すのではなく、その配列をループして配列内の各項目を呼び出すことができますpromise_array
Nick

4

最近この問題がありましたが、未知の数のプロミスがありました。jQuery.map()を使用して解決しました

function methodThatChainsPromises(args) {

    //var args = [
    //    'myArg1',
    //    'myArg2',
    //    'myArg3',
    //];

    var deferred = $q.defer();
    var chain = args.map(methodThatTakeArgAndReturnsPromise);

    $q.all(chain)
    .then(function () {
        $log.debug('All promises have been resolved.');
        deferred.resolve();
    })
    .catch(function () {
        $log.debug('One or more promises failed.');
        deferred.reject();
    });

    return deferred.promise;
}

これはjQuery.map()ではなくArray.prototype.map()(developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)ですが、このアプローチは機能します。
アナスタシア

0

ですから、チェーンの長さを知る必要があります。私が長さ10の約束があったら、私は$q.all([p1.then(..).then(...).then(...).then(...) ...]);正しくしなければならないでしょうか?
jensengar 14

0

「非同期関数」で「待機」を使用できます。

app.controller('MainCtrl', async function($scope, $q, $timeout) {
  ...
  var all = await $q.all([one.promise, two.promise, three.promise]); 
  ...
}

注:非同期関数から非同期関数を呼び出して正しい結果が得られるかどうかは、100%わかりません。

とはいえ、これがウェブサイトで使用されることは決してないでしょう。しかし、負荷テスト/統合テストについては...多分。

コード例:

async function waitForIt(printMe) {
  console.log(printMe);
  console.log("..."+await req());
  console.log("Legendary!")
}

function req() {
  
  var promise = new Promise(resolve => {
    setTimeout(() => {
      resolve("DARY!");
    }, 2000);
    
  });

    return promise;
}

waitForIt("Legen-Wait For It");

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.