javascript - 如何知道动态 "iterable"参数中的所有 Promise 何时已解决?

标签 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*/});

我这里有个问题。当前 2 个 promise 得到解决时,Promise.all 行为将被执行,但是,在这 2 个 promise 得到解决之前,添加了第三个 promise ,并且不会考虑这个新的 promise 。

所以,我需要的是这样的话:“嘿 Promise.all,你有一个动态数组要检查”。我该怎么做?

请记住,这只是一个示例。我知道我可以将 Promise.all 行移动到最后一行,但实际上,当另一个 Promise 解决时,新的 Promise 是动态添加的,并且新的 Promise 也可以添加新的 Promise,所以,它是一个真正的动态数组。

我的真实用例是这样的:

  1. 我使用 Twitter API 检查是否有新推文(使用搜索 Api)。
  2. 如果我发现新的推文,我会将其添加到 MongoDB(这里有 Promises)。
  3. 如果这些新推文与我的 MongoDB 中没有的用户相关(这里我们有新的 promise ,因为我必须去 MongoDB 来检查我是否有该用户),我们可以使用 Twitter API 来获取用户信息(更多 promise ),然后我们将这些新用户添加到 MongoDB(是的,更多 promise )。
  4. 然后,我转到 MongoDB 插入新值,将新推文与这些新用户关联起来(更多 promise !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()!
});

现在,举一个美丽的例子。当调用最后一个(我们不知道哪个是最后一个)promise 时,我们需要调用 showAllTheInformation 函数。你是怎么做到的?:

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

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

最佳答案

您可以创建一个简洁的小递归函数来包装 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!"); });

在带有 lambda 且没有注释的 ES6 中,这甚至可以更短:

function iterablePromise(iterable) {
  return Promise.all(iterable).then((resolvedIterable) => {
    if (iterable.length != resolvedIterable.length) {
      return iterablePromise(iterable);
    }
    return resolvedIterable;
  });
}

或者,如 Rads their answer 中用 async/await 表示,但作为一个函数:

async function iterablePromise(iterable) {
  let resolvedIterable = [];
  while (iterable.length !== resolvedIterable.length) {
    resolvedIterable = await Promise.all(iterable);  // implicit "then"
  }
  return resolvedIterable;
}

请记住,这仅涵盖加法,并且仍然有点危险:您需要确保回调顺序,以便任何正在运行的 promise 将自己添加到列表之前 Promises.all 可以调用回调。

关于javascript - 如何知道动态 "iterable"参数中的所有 Promise 何时已解决?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38838332/

相关文章:

javascript - 直接从 body 标签中删除未被另一个父 html 元素包裹的所需元素

javascript - 使用 .finally() 时,Angular 1.6.1 "Possibly unhandled rejection"

javascript - 无法从 api Controller js 文件获取数据以使用 Vue.js 应用程序在 sails js 中查看 ejs 文件

javascript - 我可以使用 JavaScript 在视频标记中添加或删除属性吗?

javascript - 使用 Javascript 进行待办事项列表 : can't delete item

node.js - 嵌套 Promise 不会将错误传播到 Node.js 中的父 Promise?

javascript - Service Worker - 首先是网络然后缓存回退到静态页面

javascript - 将大数组排序为更简单的数组

javascript - 如何在 ES6 中实现命名构造函数

javascript - Twitter API JSON 调用未获取最新 3 条推文