将诺言转换为异步等待时的时间

时间:2018-10-23 02:07:45

标签: javascript promise async-await

我有以下内容

return this
        .configure(config => {
          viewModel.configure(config, this);
          return config;
        })
        .then(() => {
          this.activate();
        });

,相当于以下代码块,由vscode auto promise-> async / await转换建议:

await this.configure(config => {
  viewModel.configure(config, this);
  return config;
});
this.activate();

我的问题是,他们的时间实际上是否相同?在promise示例中,then() 1个微任务中的第二个fn是否不应该远离接受config的回调?

为清楚起见,还有一个额外的问题:在计时角度上,以下条件是否相等:

Promise.all(tasks)
  .then(() => {
    othertasks.forEach(() => doStuff());
  })
  .then(() => {
    prune();
  });

和:

await Promise.all(tasks);
othertasks.forEach(() => doStuff();
await Promise.resolve();
prune();

2 个答案:

答案 0 :(得分:2)

编辑:关于Andrea Giammarchi的回答,我应该澄清一下,我的回答纯粹是并且仅与同步之间的跳动次数的差异有关。执行的代码,而不是所编写的代码的结果是否实际上是等效的(显然,未等待的promise将产生promise,而等待的promise将产生值)

在我和bigopon在github问题中进行讨论的情况下,这更有意义,他接受了VS Code的建议,该建议从一段遗留代码中从承诺链中删除“冗余” .then碰巧对细微的计时问题很敏感。

我指出,此更改将使特定方法提前一秒钟执行,而这种影响可能会破坏依赖于这些(古怪)时间的复杂应用程序。

当时的讨论是这样的,

somePromise.then(() => {
   ...
}).then(() => {
   doStuff();
})

将具有与此相同的时间:

await somePromise;
doStuff();

我的回答是:不,第二个摘录中的doStuff()会提前执行一个滴答声。

如果有人建议await.then实际上会被同步执行(如果传递的诺言已经解决的话),那激励我写这个答案并阐明为什么不这样做。

我确实意识到,没有这种上下文,我的回答似乎会引起误解,但同样:只是指出了滴答声的相似性。

原始答案

示例1

以简单的方式解析值:

await something

等效于此:

Promise.resolve(something).then()

它们都导致未完成的承诺。

示例2

对于排队任务,此:

await Promise.resolve();
doStuff();

等效于此:

Promise.resolve().then(() => {
    doStuff();
})

在两种情况下,doStuff()都发生在下一个刻度上。

要确定常规.then链是否等效于一系列await,只需计算.thenawait。如果在两个给定的代码段中每个代码的数量相同,那么在这些代码段之间传递的时间/滴答声/时间将是相同的。

示例3

另一个例子,这是

await Promise.resolve();
doStuff();
await Promise.resolve();
doStuff();
await Promise.resolve();
await Promise.resolve();
doStuff();

等效于此:

Promise.resolve()
.then(() => {
    doStuff();
})
.then(() => {
    doStuff();
})
.then(() => {})
.then(() => {
    doStuff();
})

请注意,Promise.resolve()本身对时间没有影响。它返回一个已解决的承诺。是then() / await使其变为待处理的文件。

因此,我谨对此表示不同意,我相信您的两个例子都是相同的。

规范怎么说

  1. 如果答应。[[PromiseState]]是“待处理”,则

    a。将promiseReaction追加为诺言的列表的最后一个元素。[[PromiseFulfillReactions]]。

    b。追加rejectReaction作为诺言的List的最后一个元素。[[PromiseRejectReactions]]。

  2. 否则,如果诺言。[[PromiseState]]被“履行”,则

    a。让价值成为承诺。[[PromiseResult]]。

    b。执行EnqueueJob(“ PromiseJobs”,PromiseReactionJob,«complementReaction,value»)。

这是“如果诺言已经悬而未决,只需附加该悬而未决的诺言的complementReaction,但诺言已兑现,然后加入新工作”。

换句话说,保证.then返回一个待处理的承诺,无论它所链接的承诺是否已实现

答案 1 :(得分:1)

我认为VSCode所发生的事情,您作为问题提出的问题以及收到的答案的类型都存在很多困惑。

我会尽力澄清所有问题,希望我能正确解决这个问题。

让我开始说...

这两个块不相等

以下代码:

this
  .configure(config => {
    viewModel.configure(config, this);
    return config;
  })
  .then(value => `some ${value}`);

仅与该名称“ 等效

await this
  .configure(config => {
    viewModel.configure(config, this);
    return config;
  })
  .then(value => `some ${value}`);

这是因为await的优先级低于方法链接/ then的串联。

(async function (){
  const then = await Promise.resolve(1).then(one => one + one);
  console.log(then); // this is number 2
  const something = await 123;
  console.log(something); // this is number 123
}());

您应该感到困惑的原因是VSCode超越了您的意图。

  return this
    .configure(config => {
      viewModel.configure(config, this);
      // configure() returns a Promise
      // and config can be a Promise too
      return config;
    })
    .then(() => {
      // but you are not using here the config value
      // or, if it was a promise, whatever value it resolved
      // and you are also not returning any value
      this.activate();
    });

由于VSCode知道configure thenable ,并且它的返回值也可能是Promise,这意味着activate仅在config之后才会发生最终解决,它也知道用extra勾号是没有意义的,因为您不需要返回任何config(无论是价值还是承诺),因此activate可以被称为正确离开。

由于您在最后一个then中也未返回任何值,因此可以删除整个return

// only async to wait for
await this.configure(config => {
  viewModel.configure(config, this);
  return config;
});
// with config either value or promise
// there's nothing else to wait for, so
// let's invoke activate without returning
// anything, producing is the same undefined result
this.activate();

回顾一下等待中发生的事情:

(async function (){
  const config = new Promise(res => setTimeout(res, 1000));
  console.time('awaiting');
  const value = await Promise.resolve(1).then(() => {
    return config;
  });
  console.timeEnd('awaiting');
  // awaiting: 1000.XXXms
}());

如果您有机会在最后一个then中使用返回值,您将发现VSCode无法删除它,最有可能将其重新命名为const value = await ...; this.activate(value);,这仍然可以


在上一条评论中指出:

  

以简单的方式解析值:

     

await something

     

等效于此:

     

Promise.resolve(something).then()

     

它们都导致未完成的承诺。

不确定我读错了什么,但对我来说这是一个令人误解的陈述。

const resolved = await anything表示resolved始终是一个值,而不是未完成的承诺。

这很可能是await的全部要点:它不会停止等待,直到有一个值为止。

示例:

(async function (){
  const something = Promise.resolve(Math.random());
  // this logs the random number as typeof number
  console.log(await something);
  // this also logs the random number as typeof number
  console.log(await Promise.resolve(something).then());
  // while this is one is the only pending promise
  console.log(Promise.resolve(something).then());
}());

您最终在控制台中看到Pending promise的原因是AIIFE(异步立即调用函数表达式)本身就是一个Promise,您可以在其他地方等待它。

您会看到,返回值或未完成的承诺将始终产生预期的结果。

(async function (){
  // instant return, it's just fine
  // return 123;

  // return promise (unnecessary ticks added)
  return Promise.resolve(123).then();
}()).then(console.log);

在两种情况下都记录123号。

我希望现在很清楚,特别是对于OP,VSCode中发生了什么,特别是为什么发生了。

致谢。