【问题标题】:why is using .then() 3x faster then await in my test?为什么在我的测试中使用 .then() 比 await 快 3 倍?
【发布时间】:2018-06-30 01:30:38
【问题描述】:

这完全可能是我误解了 async/await 在 Javascript 中的工作方式,但我还没有弄明白。

我有一个简单的测试,它在实际运行测试之前使用一些帮助器生成模拟数据。

这里是辅助函数:

async create(numCustomers, businessId, options) {

    options = _.assign({ type: 'customer' }, options);

    const customers = await Factory.createMany('user', options, numCustomers);

    return Promise.all(customers.map(async (c) => {
        await AccountHelper.create(businessId, c.get('id'));
        return c;
    }));
}

这里有两个版本的测试:

async/await版本:

const customers = await CustomerHelper.create(10, Constants.fakeBusinessId);

await Promise.all(customers.map(async (c) => {
    await PetHelper.create(1, c.get('id'));
}));

const res = await server.inject(request);
expect(res.statusCode).to.equal(200);

.then() 版本:

CustomerHelper.create(10, Constants.fakeBusinessId).then(async (customers) => {

    await Promise.all(customers.map(async (c) => {
        await PetHelper.create(1, c.get('id'));
    }));

    const res = await server.inject(request);
    expect(res.statusCode).to.equal(200);
});

.then() 版本在大约 2 秒内完成,async/await 版本在近 7 秒内完成。这两种形式之间的变化似乎是唯一的变量。

我在 OSX 上运行 Node 8.9.4。

我感谢任何见解或教育:)

【问题讨论】:

  • 可能一种形式并行运行,另一种形式顺序运行。
  • 您的 await 版本必须在运行测试之前创建所有客户,而您的 then 方法在每个客户返回后立即运行,因此更加并行。
  • @Turksarama - 这意味着.then 被多次调用
  • 不,然后为每个客户调用一次您传入其中的函数
  • 嗯,函数调用是一种简化,因为实际发生的情况是有一条管道正在建立,客户作为生成器。它更像是 Javascript(或者更确切地说,您的 Javascript 引擎)可以看到您正在传递一些可以生成值的东西,然后调用一个映射这些值的函数,以便它可以在引擎盖下将它们优化成一个更长的函数值而不是生成器本身。

标签: javascript


【解决方案1】:

这两种方法的持续时间应该大致相同,只要您正确地向测试框架发出测试已完成的信号。 (在大多数测试框架中,通过返回 Promise 或调用 done 回调)

如果您没有正确地向框架发出信号,那么测试将在异步处理完成之前退出(在许多情况下甚至开始)。此外,如果您的断言/期望等待异步事件,那么它们甚至不会被生成,并且没有断言的测试(在大多数框架中)是通过测试。正因为如此,async/await 正在成为测试的首选样式,因为更难(尽管并非不可能)错误地向测试框架发出信号(如您的示例所示)。

向测试框架发出测试已完成信号的正确方法(对于 mocha/jest 风格的框架):

it('does async work with await', async function (){
  let result = await asyncWork()
  assertItWorked(result)
})

it('does async work with returned promise', function (){
  return asyncWork().then(function (result) {
    assertItWorked(result)
  })
})

it('does async work with promise and callback', function (done){
  asyncWork().then(function (result) {
    assertItWorked(result)
    done() // note that this method will timeout if it fails, instead of displaying the error message, but you still get a failure
  })
})

it('does async work with promise and chained callback', function (done){
  asyncWork().then(function (result) {
    assertItWorked(result)
  }).then(done)
})

看起来几乎正确的不正当方式:

it('does async work but does not wait', function (done){
  asyncWork().then(function (result) {
    assertItWorked(result)
  })
  done() // this gets called before async work is done, and no assertions are made
})

it('does async work with promise and chained callback, but calls callback too soon', function (done){
  asyncWork().then(function (result) {
    assertItWorked(result)
  }).then(done()) // this is effectively identical to the previous example, because done gets called immediately
})

还请注意,async/await 样式完全捕获来自asyncWork 的抛出错误/拒绝,尽管其他样式也可以使用更多代码。

【讨论】:

  • 你说的完全正确。看起来一切正常,但我得到了误报,我没有兑现承诺。感谢您解决这个问题!
猜你喜欢
  • 2018-08-12
  • 2021-01-06
  • 2012-02-01
  • 2014-05-29
  • 2023-03-20
  • 2016-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多