Chờ mọi lời hứa giải quyết


107

Vì vậy, tôi có một tình huống mà tôi có nhiều chuỗi lời hứa có độ dài không xác định. Tôi muốn một số hành động chạy khi tất cả CHUỖI đã được xử lý. Điều đó thậm chí có thể? Đây là một ví dụ:

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

Trong ví dụ này, tôi thiết lập một $q.all()cho các lời hứa một, hai và ba sẽ được giải quyết vào một thời điểm ngẫu nhiên nào đó. Sau đó, tôi thêm lời hứa vào phần cuối của một và ba. Tôi muốn allgiải quyết khi tất cả các chuỗi đã được giải quyết. Đây là kết quả khi tôi chạy mã này:

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

Có cách nào để chờ các chuỗi giải quyết không?

Câu trả lời:


161

Tôi muốn tất cả giải quyết khi tất cả các chuỗi đã được giải quyết.

Chắc chắn, sau đó chỉ cần chuyển lời hứa của mỗi chuỗi vào all()thay vì những lời hứa ban đầu:

$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
Cảm ơn vì đã xác nhận nỗi sợ hãi tồi tệ nhất của tôi. Bây giờ tôi phải nghĩ ra cách để có được lời hứa cuối cùng lol.
jensengar

Vấn đề với điều đó là gì? Chuỗi của bạn có được cấu tạo động không?
Bergi

Chính xác là vấn đề của tôi. Tôi đang cố gắng tạo động một chuỗi lời hứa, sau đó tôi muốn làm điều gì đó khi (các) chuỗi hoàn thành.
jensengar

Bạn có thể cho chúng tôi xem mã của bạn (có thể hỏi một câu hỏi mới)? Có các mục được thêm vào chuỗi sau khi Q.allđược thực thi - nếu không thì nó phải là tầm thường?
Bergi

Tôi rất muốn cho bạn xem mã ... nhưng tôi vẫn chưa viết xong, tuy nhiên tôi sẽ cố gắng hết sức để giải thích nó. Tôi có một danh sách các "hành động" cần được thực hiện. Những hành động này có thể có bất kỳ cấp số lượng hành động phụ nào được liên kết với chúng. Tôi muốn có thể làm điều gì đó khi tất cả các hành động và giao dịch phụ của họ đã hoàn tất. Có thể sẽ có nhiều $q.alls, tuy nhiên khi tôi bắt đầu quá trình giải quyết, sẽ không có hành động / lời hứa mới nào được xâu chuỗi.
jensengar

16

Các câu trả lời được chấp nhận là đúng. Tôi muốn cung cấp một ví dụ để giải thích một chút cho những người không quen thuộc promise.

Thí dụ:

Trong ví dụ của tôi, tôi cần thay thế các srcthuộc tính của imgthẻ bằng các url phản chiếu khác nhau nếu có trước khi hiển thị nội dung.

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

Giải trình:

Từ tài liệu AngularJS :

Các thenphương pháp:

sau đó (successCallback, errorCallback, InformCallback) - bất kể khi nào lời hứa được hoặc sẽ được giải quyết hoặc bị từ chối, sau đó gọi một trong các lệnh gọi lại thành công hoặc lỗi không đồng bộ ngay khi có kết quả. Các lệnh gọi lại được gọi với một đối số duy nhất : kết quả hoặc lý do từ chối.

$ q.all (lời hứa)

Kết hợp nhiều lời hứa thành một lời hứa duy nhất được giải quyết khi tất cả các lời hứa đầu vào được giải quyết.

Tham số promisescó thể là một loạt các lời hứa.

Giới thiệu bind(), Thông tin thêm tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind


Các thenphương pháp $q.allđược cung cấp một loạt các lời hứa trở lại, vì vậy bạn có thể lặp rằng mảng và cuộc gọi thentrên từng hạng mục trong mảng, như trái ngược với gọi thenkhi bạn thêm lời hứa promise_array.
nick

4

Gần đây đã gặp sự cố này nhưng với số lượng lời hứa không xác định. Giải quyết bằng cách sử dụng 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;
}

Nó không phải jQuery.map () mà là Array.prototype.map () ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) nhưng cách tiếp cận này hoạt động.
Anastasia

0

Điều đó đòi hỏi tôi phải biết chiều dài của chuỗi của tôi mặc dù phải không? Ý tôi là nếu tôi có một lời hứa dài 10, tôi sẽ phải làm $q.all([p1.then(..).then(...).then(...).then(...) ...]);đúng chứ?
jensengar

0

Bạn có thể sử dụng "await" trong một "hàm không đồng bộ" .

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

LƯU Ý: Tôi không chắc chắn 100% rằng bạn có thể gọi một hàm không đồng bộ từ một hàm không đồng bộ và có kết quả phù hợp.

Điều đó nói rằng điều này sẽ không bao giờ được sử dụng trên một trang web. Nhưng đối với thử nghiệm tải / thử nghiệm tích hợp ... có thể.

Mã ví dụ:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.