如何编写任意长的Promise链

时间:2018-07-08 17:34:56

标签: javascript node.js asynchronous promise

我收到一个对象bigListFromClient,其中包含任意数量的对象,每个对象可以具有任意数量的子代。每个对象都需要输入到我的数据库中,但是数据库需要为每个对象分配一个唯一的ID,子对象在发送给数据库之前必须具有其父对象的唯一ID。

我想创建某种Promise或其他调用结构,这些结构将异步调用自己,直到到达bigListFromClient中的最后一个对象为止,但是我在弄清楚如何编写它上遇到了麻烦。

for(let i = 0; i < bigListFromClient.length; i++){
  makeDbCallAsPromise(bigListFromClient[i].queryString, console.log); //I'm not just accepting anything from a user here, but how I get my queryString is kind of out of scope for this question
  for(let j = 0; j < bigListFromClient[i].children.length; j++){
    //the line below obviously doesn't work, I'm trying to figure out how to do this with something other than a for loop
    makeDbCallAsPromise(bigListFromClient[i].children[j].queryString + [the uniqueID from the DB to insert this correctly as a child], console.log);
  }
}


//this promise works great
makeDbCallAsPromise = function(queryString){
    return new Promise((resolve, reject) => {
        connection = mysql.createConnection(connectionCredentials);

        connection.connect();
        query = queryString;
        connection.query(query, function (err, rows, fields) {
            if (!err) {
                resolve(rows);
            } else {
                console.log('Error while performing Query.');
                console.log(err.code);
                console.log(err.message);
                reject(err);
            }
        });
        connection.end();
    })
};

我自己解决这个问题的尝试非常尴尬,以至于向您描述它们都是可怕的。

虽然我可以将所有创建孩子的电话推迟到在数据库中创建父母之前,但我想知道我所描述的方法是否可行。

2 个答案:

答案 0 :(得分:1)

基本上有两种方法可以做到这一点。一种是顺序进行数据库调用,另一种是并行进行调用。

Javascript具有一个名为Promise.all的并行内置函数,您向它传递了一个Promise实例数组,并返回一个包含该数组的Promise实例。

在您的情况下,您的代码应如下所示:

const result = Promise.all(
  bigListFromClient.map(item => 
    makeDbCallAsPromise(item.queryString).then(result =>
      Promise.all(
        item.children.map(item => 
          makeDbCallAsPromise(item.queryString + [result.someId])
        )
      )
    ])
  })

result现在将包含一个解析为数组数组的Promise。这些数组包含插入子级的结果。

使用更现代的方法(使用async await),按顺序进行操作,并将所有结果组合成一个平面数组:

 const result = await bigListFromClient.reduce(
  async (previous, item) => {
    const previousResults = await previous
    const result = await makeDbCallAsPromise(item.queryString)
    const childResults = await item.children.reduce(
      async (result, item) =>
        [...(await result), await makeDbCallAsPromise(item.queryString + [result.someId])],
      []
    )
    return [...previousResults, result, ...childResults)
  ]),
  []
})

根据要实现的目标以及要如何构造代码,可以从不同的方法中进行选择。

答案 1 :(得分:0)

对于这种操作,请尝试查看bulk inserting。如果您打算在每次迭代中执行一次数据库查询/事务,请在每个父级上递归循环和/或对每个子级执行相同的过程。

const dbCall = async (elm) => {
  elm.id = Math.random().toString(36).substring(7)
  if (elm.children) {
    await Promise.all(elm.children.map(child => {
      child.parentId = elm.id
      return dbCall(child)
    }))
  }
  return elm
}

const elms = [
  {
    queryString: '',
    children: [
      {
        queryString: ''
      }
    ]
  }
]

Promise.all(elms.map(dbCall)).then(elm => /* ... */)