使用await关键字与在异步函数中省略它有什么区别?

时间:2019-09-11 20:38:02

标签: javascript async-await

使用await关键字与不捕获{​​{1}}调用的返回值时省略关键字有何不同?

例如,创建这四个文件的方式是否存在差异?因为如果我运行此命令,则会创建所有这四个命令,所以我想知道。

await

更新:

我尝试了一些测试,看起来它的行为符合回答,但最后,情况变得很奇怪。

因此,如果您运行以下命令,

const fs = require('fs');
const { promisify } = require('util');
const writeFile = promisify(fs.writeFile);

(async function () {
    await writeFile('a.txt', 'aaa');
    await writeFile('b.txt', 'bbb');
    writeFile('c.txt', 'ccc');
    writeFile('d.txt', 'ddd');
})()

输出有时为writeFile('a.txt', 'foo'); writeFile('a.txt', 'bar'); const content = await readFile('a.txt', 'utf8'); console.log(content); ,有时为foo,这似乎同时触发了两个bar调用,最后完成的时间取决于运气。

所以我将其更改为:

writeFile

并且我认为(基于答案)这将始终输出writeFile('a.txt', 'foo'); await writeFile('a.txt', 'bar'); const content = await readFile('a.txt', 'utf8'); console.log(content); ,但是再次输出有时是bar,有时是foo
我不确定我是否理解,因为我以为我已经等待写bar的呼叫,因此唯一的解释是第一个bar呼叫在等待的呼叫后以某种方式轮到了。

然后我不得不在通话之间设置延迟:

writeFile

这一次它总是输出writeFile('a.txt', 'foo'); [...Array(400000).fill('hello')].join('\n') writeFile('a.txt', 'bar'); const content = await readFile('a.txt', 'utf8'); console.log(content);

然后发生了一些奇怪的事情。
如果运行以下命令:

bar

输出为writeFile('a.txt', 'foo'); [...Array(40000).fill('hello')].join('\n') writeFile('a.txt', 'b'); const content = await readFile('a.txt', 'utf8'); console.log(content); ,我能想到的唯一解释是第一个boowriteFile写入文件,然后第二个foo开始写入文件,但在完成操作之前,等待的writeFile会读取文件。 (笑)

3 个答案:

答案 0 :(得分:2)

然后它不会await结果。它将触发并忘记异步任务。在大多数情况下,这不是您想要的,因为代码乱序,并且错误未得到处理

 await writeFile(); // If an error occurs in writeFile, the promise returned by this function will reject too
 await writeFile(); // this will definetly run after the previous call completed.

答案 1 :(得分:1)

在此示例代码中:

writeFile('a.txt', 'foo');
await writeFile('a.txt', 'bar');
const content = await readFile('a.txt', 'utf8');
console.log(content);

第一个writeFile可能会随时等待完成,因为它没有被等待。您是否在等待写入bar都没关系;这仅表示您的读取在第二次写入后完成。第一次写入可能会在此之后完成。这就是readFile可以是foobar的原因。

也许可以通过显示未表示为异步函数的等效代码来可视化:

function doTheThing() {
  writeFile('a.txt', 'foo');
  return writeFile('a.txt', 'bar')
    .then(() => {
      // This happened after writing 'bar', but 'foo' may/may not have finished
      return readFile('a.txt', 'utf8');
    })
    .then(content => {
      console.log(content);
    });
}

这是所谓的竞争条件。想想平行运行的轨迹:

                  File state

writeFile 'foo'               writeFile 'bar'
      |--- A -->                    |
      |              bar <----------|
      |--- B -->                    v
      |--- C -->                readFile
      |                             |
      |               ?  ---------->|
      |                             v
      |                       console.log()
      |--- D -->

因为我们不等待第一次写入,所以它可能在A,B,C,D点完成,或者可能永远不会完成(如果失败或需要很长时间)。

  • 如果它在A点完成,那么'bar'将覆盖'foo'
  • 如果它在B点完成,那么'foo'将覆盖'bar'
  • 如果它在C点的读取过程中完成,您甚至可以读取部分写入内容!

保证写顺序的唯一方法是使await保持顺序:

await writeFile('a.txt', 'foo');
await writeFile('a.txt', 'bar');
const content = await readFile('a.txt', 'utf8');
console.log(content); // Always 'bar'

对于此代码块:

writeFile('a.txt', 'foo');
[...Array(40000).fill('hello')].join('\n')
writeFile('a.txt', 'b');
const content = await readFile('a.txt', 'utf8');
console.log(content);

...您是正确的,因为无论如何您都不在等待writeFile,因此readFile可能正在写入文件时正在读取文件。

答案 2 :(得分:0)

如果您使用的是结果值,或者必须等到await下的函数被执行,则在这种情况下需要

await

await停止执行主线程,直到await下的函数返回。