Tunggu sampai semua janji diselesaikan

107

Jadi saya memiliki situasi di mana saya memiliki banyak rantai janji dengan panjang yang tidak diketahui. Saya ingin beberapa tindakan dijalankan ketika semua CHAINS telah diproses. Apakah itu mungkin? Berikut ini contohnya:

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);
});

Dalam contoh ini, saya menyiapkan $q.all()untuk promise satu, dua, dan tiga yang akan diselesaikan pada waktu acak. Saya kemudian menambahkan janji di akhir satu dan tiga. Saya ingin allmenyelesaikan ketika semua rantai telah diselesaikan. Berikut adalah outputnya ketika saya menjalankan kode ini:

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

Apakah ada cara untuk menunggu rantai itu terpecahkan?

Jensengar
sumber

Jawaban:

161

Saya ingin semuanya diselesaikan ketika semua rantai telah diselesaikan.

Tentu, maka cukup berikan janji setiap rantai ke dalam all()alih - alih janji awal:

$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");
});
Bergi
sumber
2
Terima kasih telah mengkonfirmasi ketakutan terburuk saya. Sekarang saya harus menemukan cara untuk mendapatkan janji terakhir lol.
jensengar
Apa masalahnya dengan itu? Apakah rantai Anda dibuat secara dinamis?
Bergi
Persis masalah saya. Saya mencoba untuk membuat rantai janji secara dinamis, lalu saya ingin melakukan sesuatu saat rantai tersebut selesai.
jensengar
Bisakah Anda menunjukkan kode Anda (mungkin mengajukan pertanyaan baru)? Apakah ada item yang ditambahkan ke rantai setelah Q.alldieksekusi - jika tidak, itu harus sepele?
Bergi
Saya ingin menunjukkan kepada Anda kodenya ... tetapi saya belum selesai menulisnya, namun saya akan melakukan yang terbaik untuk menjelaskannya. Saya memiliki daftar "tindakan" yang perlu dilakukan. Tindakan ini mungkin memiliki sejumlah tingkat sub-tindakan yang terkait dengannya. Saya ingin dapat melakukan sesuatu ketika semua tindakan dan subaksinya telah selesai. Kemungkinan akan ada beberapa $q.all, namun begitu saya memulai proses penyelesaian, tidak ada tindakan / janji baru yang akan dirangkai.
jensengar
16

The jawaban yang diterima adalah benar. Saya ingin memberikan contoh untuk menjelaskan sedikit kepada mereka yang tidak terbiasa promise.

Contoh:

Dalam contoh saya, saya perlu mengganti srcatribut imgtag dengan url mirror yang berbeda jika tersedia sebelum merender konten.

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.
);

Penjelasan:

Dari dokumen AngularJS :

The thenMetode:

then (successCallback, errorCallback, notifyCallback) - terlepas dari kapan janji itu atau akan diselesaikan atau ditolak, kemudian memanggil salah satu callback keberhasilan atau kesalahan secara asinkron segera setelah hasilnya tersedia. Callback dipanggil dengan satu argumen : alasan hasil atau penolakan.

$ q.all (janji)

Menggabungkan beberapa janji menjadi satu janji yang diselesaikan saat semua janji masukan diselesaikan.

The promisesparam dapat array janji.

Tentang bind(), Info selengkapnya di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Hieu
sumber
The thenMetode $q.alldisediakan berbagai janji-janji kembali, sehingga Anda dapat loop bahwa array dan panggilan thenpada setiap item dalam array, sebagai lawan menelepon thenketika Anda menambahkan janji untuk promise_array.
nick
4

Baru-baru ini mengalami masalah ini tetapi dengan jumlah promise yang tidak diketahui . Dipecahkan menggunakan 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;
}
SoniCue
sumber
Ini bukan jQuery.map () tetapi Array.prototype.map () ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) tetapi pendekatan ini berhasil.
Anastasia
0

Ada jalan. $q.all(...

Anda dapat memeriksa barang-barang di bawah ini:

http://jsfiddle.net/ThomasBurleson/QqKuk/

http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/

Nikola Yovchev
sumber
Itu mengharuskan saya untuk mengetahui panjang rantai saya kan? Maksud saya jika saya memiliki janji panjang 10, saya harus melakukannya $q.all([p1.then(..).then(...).then(...).then(...) ...]);kan?
jensengar
0

Anda bisa menggunakan "await" dalam "fungsi async" .

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

CATATAN: Saya tidak 100% yakin Anda dapat memanggil fungsi async dari fungsi non-async dan mendapatkan hasil yang benar.

Yang mengatakan ini tidak akan pernah digunakan di situs web. Tetapi untuk pengujian beban / pengujian integrasi ... mungkin.

Kode contoh:

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");

Flavouski
sumber