使用 Promise.map 的每个承诺值作为下一个循环的输入

Using each promise value of Promise.map as input of the next loop

我已经做了一整天的研究,研究如何在 Promise.map 中获得每个承诺的结果,并在相同 Promise.map 的循环中将其用作下一次迭代的输入。我 严格地 需要执行此方法,因为我在 数据库操作 中使用此逻辑 必须是原子的 如果有任何承诺被拒绝,所有以前的交易都必须回滚。

注意: 我已经使用了 promise.each 并且它运行良好只是它不允许我关联各个承诺并回滚 all 如果一个失败。所以 Promise.map 似乎是最好的解决方案,当每个 promise 都被仔细地解决并且返回值时不会在下一个循环中导致 Error: Transaction query already complete 。这是 knex 的逻辑:

var obj={};

knex.transaction(function(trx) {
  return Promise.map(array, function(item) {
        return trx.insert(item).into('table')
        .then(returnedFields => {
        //do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
        //[START EDIT: this responds to comment by @ Mikael Lepistö for clarity]
        //update obj here to be used in next loop
        //[END EDIT]
      });
    }, {concurrency: 1});
})
.then(function(inserts) {
  console.log(inserts.length + 'Items saved.');
})
.catch(function(error) {
  console.error(error);
})

看看这个:

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

您可以查看 async.js 图书馆 https://caolan.github.io/async/docs.html

您可以执行以下操作

knex.transaction(function(trx) {
    return async.mapValuesSeries(array, (item) => {
        return trx.insert(item).into('table')
        .then(returnedFields => {
        //do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
        });
    }, (error, inserts) => {
        if (err) { 
            console.error(error);
        }
        console.log(inserts.length + 'Items saved.');
    });
});

感谢所有发布 great/greater 见解的人。然而,事实证明 Promise.eachknex 配合得很好,我在循环期间错误地调用了 promisesthen 内部的 commit 因此在后续循环的下一次事务尝试中导致 Error: Transaction query already complete原因: 无需使用 knex 事务 context/block 调用 commit,因为它会在该上下文中自动触发。

要在下面的回答中注明:

KnexPromise.each 一起使用需要您在每个承诺的 then 块内听取可能的拒绝,并使用 try/catch 并且在某些情况下 明确拒绝否则后续promises/values将继续循环并且不能使数据库成为原子!!!

knex.transaction(function(trx) {
  return Promise.map(array, function(item) {
        return trx.insert(item).into('table')
        .then(returnedFields => {
        try{
        //do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
        /* START Testing a case of rejection */
           if (array.indexOf(item) === 3) {
               //uncomment the code below to test
               //throw new Error('BreakException');
            }
        /* END */
        }catch(err){
        fail=true;
        }
      }).then(val => {
          if (fail) {
            trx.rollback()//you can ignore this as is automatically triggered here - tested
            return Promise.reject(new Error('Rejected'))
          }
          return val
        })
        .catch(err => {
          fail = true
          trx.rollback()//you can ignore this as is automatically triggered here - tested
          return Promise.reject(new Error('Rejected'))
        });
    });
})
.then(function(inserts) {
  console.log(inserts.length + 'Items saved.');
})
.catch(function(error) {
  console.error(error);
})