2014-02-13 123 views
98

所以我有一個情況我有一個未知長度的多個承諾鏈。我希望在處理完所有的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 

有沒有辦法等待鏈的解決?

回答

142

我想在所有的連鎖店都已經解決了所有來解決。

當然,那麼就通過每個鏈的承諾到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

感謝您確認我最可怕的恐懼。現在我必須想出一個辦法讓最後的承諾大聲笑。 –

+0

那有什麼問題?你的鏈是動態構建的嗎? – Bergi

+0

準確地說我的問題。我試圖動態地創建一個承諾鏈,然後我想在鏈完成時做一些事情。 –

15

accepted answer是正確的。我想提供一個例子來對那些不熟悉promise的人進行詳細說明。

例子:

在我的例子,我需要的,如果提供呈現內容之前更換的img標籤與不同的鏡像網址src屬性。

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方法:

然後(successCallback,errorCallback,notifyCallback) - 無論何時承諾是的或將被解決或拒絕,然後撥打 一旦 結果可用,異步回調成功或錯誤回調之一。回調被稱爲單 論點:結果或拒絕的原因。

$ q.all(承諾)

將多個承諾變成一個承諾,當 所有輸入承諾的決心,得到解決。

參數promises可以是一組承諾。

關於bind(),這裏更多的信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

+0

'$ q.all'的'then'方法提供了一個返回的promise的數組,因此您可以循環該數組並在數組中的每個元素上調用'then',而不是在添加時調用'then'承諾'promise_array'。 – nick

3

最近有這個問題,但與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; 
} 
+1

我在代碼中看不到jQuery的任何用法? – Bergi

+0

它不是jQuery.map(),而是Array.prototype.map()(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map),但這種方法可行。 – Anastasia

0

您可以使用"await" in an "async function"

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