รอให้ทุกคำสัญญาคลี่คลาย


107

ดังนั้นฉันจึงมีสถานการณ์ที่ฉันมีโซ่สัญญาหลายอันที่ไม่ทราบความยาว ฉันต้องการให้ดำเนินการบางอย่างเมื่อ CHAINS ทั้งหมดได้รับการประมวลผลแล้ว เป็นไปได้หรือไม่? นี่คือตัวอย่าง:

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()สำหรับคำสัญญาหนึ่งสองและสามซึ่งจะได้รับการแก้ไขในบางเวลาแบบสุ่ม จากนั้นฉันก็เพิ่มคำสัญญาลงในตอนท้ายของหนึ่งและสาม ฉันต้องการ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
ขอบคุณที่ยืนยันความกลัวที่เลวร้ายที่สุดของฉัน ตอนนี้ฉันต้องหาวิธีที่จะได้รับสัญญาสุดท้าย lol
jensengar

มีปัญหาอะไร โซ่ของคุณสร้างแบบไดนามิกหรือไม่?
Bergi

ตรงกับปัญหาของฉัน ฉันพยายามสร้างห่วงโซ่สัญญาแบบไดนามิกจากนั้นฉันต้องการทำอะไรบางอย่างเมื่อโซ่เสร็จสมบูรณ์
jensengar

คุณสามารถแสดงรหัสของคุณให้เราเห็นได้หรือไม่ (อาจถามคำถามใหม่) มีรายการต่อท้ายโซ่หลังจากQ.allถูกดำเนินการหรือไม่มิฉะนั้นมันควรจะเป็นเรื่องเล็กน้อย?
Bergi

ฉันชอบที่จะแสดงรหัสให้คุณเห็น ... แต่ฉันยังเขียนไม่เสร็จอย่างไรก็ตามฉันจะพยายามอธิบายให้ดีที่สุด ฉันมีรายการ "การดำเนินการ" ที่ต้องทำ การดำเนินการเหล่านี้อาจมีระดับของการดำเนินการย่อยที่เกี่ยวข้อง ฉันต้องการที่จะทำบางสิ่งบางอย่างเมื่อการดำเนินการและการดำเนินการทั้งหมดเสร็จสมบูรณ์ อาจจะมีหลายรายการ$q.allอย่างไรก็ตามเมื่อฉันเริ่มกระบวนการแก้ปัญหาแล้วจะไม่มีการผูกมัดการกระทำ / สัญญาใหม่
jensengar

16

คำตอบที่ได้รับการยอมรับเป็นที่ถูกต้อง promiseผมอยากจะให้เป็นตัวอย่างเพื่ออธิบายรายละเอียดอีกเล็กน้อยให้กับผู้ที่ไม่คุ้นเคยกับ

ตัวอย่าง:

ในตัวอย่างของฉันฉันต้องแทนที่srcแอตทริบิวต์ของimgแท็กด้วยมิเรอร์ 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 :

thenวิธีการ:

จากนั้น (successCallback, errorCallback, informCallback) - ไม่ว่าสัญญาจะได้รับการแก้ไขหรือปฏิเสธเมื่อใดก็ตามจากนั้นเรียกหนึ่งในการโทรกลับสำเร็จหรือข้อผิดพลาดแบบอะซิงโครนัสทันทีที่มีผลลัพธ์ การเรียกกลับถูกเรียกด้วยอาร์กิวเมนต์เดียว : ผลลัพธ์หรือเหตุผลการปฏิเสธ

$ q.all (สัญญา)

รวมคำสัญญาหลาย ๆ คำไว้ในคำสัญญาเดียวที่ได้รับการแก้ไขเมื่อคำสัญญาที่ป้อนเข้าทั้งหมดได้รับการแก้ไข

promisesพระรามสามารถอาร์เรย์ของสัญญา

เกี่ยวกับbind()ข้อมูลเพิ่มเติมที่นี่: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind


thenวิธีการในการ$q.allให้บริการอาร์เรย์ของสัญญากลับไปเพื่อให้คุณสามารถห่วงที่หลากหลายและการโทรthenในแต่ละรายการในอาร์เรย์เมื่อเทียบกับการเรียกร้องเมื่อคุณเพิ่มสัญญาว่าจะthen promise_array
ชื่อเล่น

4

เมื่อเร็ว ๆ นี้มีปัญหานี้ แต่มีจำนวนไม่ทราบสาเหตุของ promises.Solved ใช้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/… ) แต่วิธีนี้ใช้ได้ผล
Anastasia

0

มีวิธี $q.all(...

คุณสามารถตรวจสอบเนื้อหาด้านล่าง:

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

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


นั่นทำให้ฉันต้องรู้ความยาวของโซ่ใช่ไหม ฉันหมายความว่าถ้าฉันมีสัญญาความยาว 10 ฉันจะต้องทำ$q.all([p1.then(..).then(...).then(...).then(...) ...]);ใช่มั้ย?
jensengar

0

คุณสามารถใช้"รอคอย" ใน "ฟังก์ชั่น async"

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

หมายเหตุ: ฉันไม่แน่ใจ 100% ว่าคุณสามารถเรียกใช้ฟังก์ชัน async จากฟังก์ชันที่ไม่ใช่ async และได้ผลลัพธ์ที่ถูกต้อง

ที่กล่าวว่าสิ่งนี้จะไม่ถูกใช้บนเว็บไซต์ แต่สำหรับการทดสอบโหลด / การทดสอบการรวม ...

รหัสตัวอย่าง:

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.