如何知道所有Promise何时被动态"可迭代"参数解析?

Bro*_*oel 11 javascript promise ecmascript-6

我的问题是我不知道如何知道动态promise数组何时解决了所有的promise.

这是一个例子:

var promiseArray = [];
promiseArray.push(new Promise(){/*blablabla*/});
promiseArray.push(new Promise(){/*blablabla*/});
Promise.all(promiseArray).then(function(){
    // This will be executen when those 2 promises are solved.
});
promiseArray.push(new Promise(){/*blablabla*/});
Run Code Online (Sandbox Code Playgroud)

我这里有问题.当前Promise.all两个承诺被解决时,行为将被执行,但是,在这两个承诺被解决之前,第三个承诺被添加并且这个新承诺将不被考虑.

所以,我需要的是:"嘿Promise.all,你有一个动态数组来检查".我该怎么做?

请记住,这只是一个例子.我知道我可以将线Promise.all移到最后一行,但实际上新的承诺是在另一个承诺解决时动态添加的,而新的承诺也可以添加新的承诺,因此,它是一个非常动态的数组.

我拥有的真实用例是这样的:

  1. 我使用Twitter API检查是否有新的推文(使用搜索Api).
  2. 如果我发现新的推文,我将它添加到MongoDB(这里我们有Promises).
  3. 如果这些新推文与我在MongoDB中没有的用户相关(这里我们有新的承诺,因为我必须去MongoDB检查我是否有该用户),我们转到Twitter API获取用户信息(更多承诺)我们将这些新用户添加到MongoDB(是的,更多的承诺).
  4. 然后,我去MongoDB插入新的值来将新推文和那些新用户联系起来(更多的承诺!wiii!).
  5. 当所有对MongoDB的查询都已解决(所有选择,更新,插入)时,关闭MongoDB连接.

另一个难的例子:

var allPromises = [];

allPromises.push(new Promise(function(done, fail){
    mongoDB.connect(function(error){
        //Because mongoDB works with callbacks instead of promises
        if(error)
            fail();
        else
            ajax.get('/whatever').then(function(){
                if (somethingHappens) {
                    allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                        // bla bla bla
                        if (somethingHappens) {
                            allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                                // bla bla bla
                            }));
                        } else {
                            ajax.get('/whatever/2').then(function(){
                                if (somethingHappens) {
                                    allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                                        // bla bla bla
                                    }));
                                }
                            });
                        }
                    }));
                } else {
                    ajax.get('/whatever/2').then(function(){
                        if (somethingHappens) {
                            allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                                // bla bla bla
                                    if (somethingHappens) {
                                        allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                                            // bla bla bla
                                        }));
                                    } else {
                                        ajax.get('/whatever/2').then(function(){
                                            if (somethingHappens) {
                                                allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
                                                    // bla bla bla
                                                }));
                                            }
                                        });
                                    }
                            }));
                        }
                    });
                }
            });
    });
}));

Promise.all(allPromises).then(function(){
    // Soooo, all work is done!
    mongodb.close()!
});
Run Code Online (Sandbox Code Playgroud)

所以,现在,一个美容的例子.我们需要在调用showAllTheInformation最后一个(我们不知道哪个是最后一个)promise时调用该函数.你怎么做呢?:

var name = 'anonimus';
var date = 'we do not know';

function userClikOnLogIn() {
    $http.get('/login/user/password').then(function(data){
        if (data.logguedOk) {
            $http.get('/checkIfIsAdmin').then(function(data){
                if (data.yesHeIsAnAdmin) {
                    $http.get('/getTheNameOfTheUser').then(function(data){
                        if(data.userHasName) {
                            $http.get('/getCurrentDate').then(function(data){
                                currentDate = data.theNewCurrentDate;
                            });
                        }
                    });
                }
            });
        }
    });
}

function showAllTheInformation() {
    alert('Hi ' + name + ' today is:' + date);
}
Run Code Online (Sandbox Code Playgroud)

这里有更多上下文的另一个例子:https: //jsfiddle.net/f0a1s79o/2/

Jef*_*ica 11

你可以创建一个简洁的小递归函数来包装Promise.all来处理原始promise的补充:

/**
 * Returns a Promise that resolves to an array of inputs, like Promise.all.
 *
 * If additional unresolved promises are added to the passed-in iterable or
 * array, the returned Promise will additionally wait for those, as long as
 * they are added before the final promise in the iterable can resolve.
 */
function iterablePromise(iterable) {
  return Promise.all(iterable).then(function(resolvedIterable) {
    if (iterable.length != resolvedIterable.length) {
      // The list of promises or values changed. Return a new Promise.
      // The original promise won't resolve until the new one does.
      return iterablePromise(iterable);
    }
    // The list of promises or values stayed the same.
    // Return results immediately.
    return resolvedIterable;
  });
}

/* Test harness below */

function timeoutPromise(string, timeoutMs) {
  console.log("Promise created: " + string + " - " + timeoutMs + "ms");
  return new Promise(function(resolve, reject) {
    window.setTimeout(function() {
      console.log("Promise resolved: " + string + " - " + timeoutMs + "ms");
      resolve();
    }, timeoutMs);
  });
}

var list = [timeoutPromise('original', 1000)];
timeoutPromise('list adder', 200).then(function() {
  list.push(timeoutPromise('newly created promise', 2000));
});
iterablePromise(list).then(function() { console.log("All done!"); });
Run Code Online (Sandbox Code Playgroud)

请记住,这仅涵盖除,而且它仍然是一个有点危险:你需要确保回调整理是这样的,在飞行中任何承诺将自己添加到列表中之前Promises.all可调用回调.


Ber*_*rgi 2

没有出路。在调用之前,您必须将所有承诺放入数组中Promise.all。在您提供的示例中,这就像将最后一行移到顶部一样简单。

如果您异步填充数组,您应该获得该数组的承诺,并使用.then(Promise.all.bind(Promise)). 如果您不知道何时停止添加承诺,那么无论如何这是不可能的,因为它们可能永远不会全部得到解决。


关于您的“美丽示例”,您将想要了解链接的魔力。正如我之前在评论中所说,您必须return对每个执行异步操作的函数做出承诺。事实上,只需添加缺少的returns:

function userClikOnLogIn() {
    return $http.get('/login/user/password').then(function(data){
//  ^^^^^^
        if (data.logguedOk) {
            return $http.get('/checkIfIsAdmin').then(function(data){
//          ^^^^^^
                if (data.yesHeIsAnAdmin) {
                    return $http.get('/getTheNameOfTheUser').then(function(data){
//                  ^^^^^^
                        if(data.userHasName) {
                            return $http.get('/getCurrentDate').then(function(data){
//                          ^^^^^^
                                currentDate = data.theNewCurrentDate;
                            });
                        }
                    });
                }
            });
        }
    });
}

userClikOnLogIn().then(function showAllTheInformation() {
//               ^^^^^ now you can chain onto it!
    alert('Hi ' + name + ' today is:' + date);
});
Run Code Online (Sandbox Code Playgroud)

这里没有动态增长的承诺数组,只是每个函数都为其所做的事情的(异步)结果返回一个承诺。

  • 很抱歉,但我认为这是一个糟糕的答案。您建议OP重现[末日金字塔](http://www.telerik.com/blogs/what-is-the-point-of-promises),这是第一个介绍的主要原因承诺之一地方。 (2认同)
  • @Roque:不,你错了。这不是厄运的回调金字塔。这里 Promise 被正确地用作返回值 - 您看到的金字塔只是嵌套的 if 块,即使没有 Promise,这自然也会导致缩进。这正是我们想要的控制流。当然,您也可以使用异常,但这两种方法都不是“坏”或错误的。 (2认同)