【问题标题】:What's the promise chaining equivalent of awaiting multiple async functions?等待多个异步函数的承诺链等价物是什么?
【发布时间】:2018-07-24 22:44:55
【问题描述】:

我正在研究 Promsies 和 async/await 的用法。

我编写了以下代码,它执行以下操作:

  1. 它获取一些数据库的数据(使用 Knex.js),
  2. 处理该数据,
  3. 将处理的数据分配给指定的属性。

这 3 个步骤被多次执行(在下面的代码中,它被执行了两次),并且总是awaited:

async function run() {
   return await getData();
}
async function getData() {
    let handledData = {};
    handledData.res1 = await knex.select('column1').from('table1').where('column1', '1')
                                 .then(data => handleData(data))
                                 .catch(handleError);
    handledData.res2 = await knex.select('column1').from('table1').where('column1', '2')
                                 .then(data => handleData(data, handledData))
                                 .catch(handleError);
    return handledData;
}
async function handleData(data) {
    let res = [];
    data.forEach(item => {
        res.push(item.column1);
    });
    return res; 
}
function handleError (error) {
    console.log(error);
}

现在,我正在尝试编写与 getData 等效的承诺链,这就是我想出的:

async function getData() {
    let handledData = {};
    let promise = new Promise(function(resolve, error){ resolve(); });
    promise
    .then(function () {
        return knex.select('column1').from('table1').where('column1', '1')
                    .then(data => handleData(data))
                    .catch(handleError);
    })
    .then(function(handled){
        handledData.res1 = handled;
        return knex.select('column1').from('table1').where('column1', '2')
                    .then(data => handleData(data))
                    .catch(handleError);
    })
    .then(function(handled){
        handledData.res2 = handled;
        return handledData;
    })
    .catch(handleError);
    return promise;
}

但这并不完全奏效。发生的情况是,在第一个 then 返回后,run 内部的 await 结束等待,这导致 run 返回 - 然后才执行第二个 then

如何使 promise-chaining 版本像多等待版本一样工作?

(请随时指出我对 promises/async-await 的任何误解)

【问题讨论】:

  • 您是否需要使用await 或顺序请求?使用Promise.all 可能会更好(并行请求,而不是串行请求)
  • @CertainPerformance,这是个好主意,我会这样做

标签: javascript ecmascript-6 promise async-await chaining


【解决方案1】:

如果可能的话,我建议改用Promise.all,它会让你的脚本运行得更快,而且逻辑更清晰:

const getData = Promise.all([
  knex.select('column1').from('table1').where('column1', '1')
    // Simply pass the function name as a parameter to the `.then`:
    .then(handleData)
    .catch(handleError),
  knex.select('column1').from('table1').where('column1', '2')
    .then(handleData)
    .catch(handleError)
])
  .then(([res1, res1]) => ({ res1, res2 }));

【讨论】:

  • 我喜欢这样比按顺序运行查询要好得多,因为你不相互依赖。
【解决方案2】:

knex.select().then() 返回一个promise,所以你不需要将它包装在另一个promise 中,你只需要设置then()s 的链并返回整个东西。结果将是 getData 从最后一个 then 返回承诺。您可以从 then() 返回所需的值,这将使其可供调用者使用。例如:

function run() {
    getData()
    .then(handledData => console.log(handledData) /* do something with data */)
}

function getData() {
    let handledData = {};
    // need to return this promise to callers can access it
    return knex.select('column1').from('table1').where('column1', '1')
    .then(data => handledData.res1 = handleData(data))
    .then(() => knex.select('column1').from('table1').where('column1', '2'))
    .then(data => {
        handledData.res2 = handleData(data)
        return handledData
    })
    .catch(handleError);
}

您也可以将其设置为通过链传递handledData 对象,但在这种情况下您不需要。

函数handleData()是同步的,所以你不需要把它变成异步函数。

【讨论】:

  • 谢谢。 2 个问题:(1)run 不会立即返回(而不是在我的原始代码上等待它)? (2) 我使用包装使代码结构化,这样每个then 都包含一个查询和处理程序(即一个then 和一个catch)。您的建议使它们“交替”:一个then 进行查询,另一个处理其结果,依此类推(想想有两个以上查询的情况)。你不觉得它不那么结构化/干净吗?
  • @HeyJude 是的,run() 将立即开始 - then 中的代码将等待查询完成。它会调用run() 你应该return getData().then() 这将返回一个承诺,就像你的异步函数一样。这里的结构仍然有一个then() for each query. The second select does use anything returned from the first, it's just chained so it runs in order like your async` 版本。我个人更喜欢async 版本,但可能会尝试同时运行这两个查询,因为它们不相互依赖。
猜你喜欢
  • 2018-02-03
  • 2020-11-19
  • 2018-10-17
  • 2021-11-09
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多