2014-02-13 74 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");