user2263572
user2263572

Reputation: 55

What's the difference between using await keyword versus omitting it in an async function?

How is using await keyword is different than omitting it when you don't capture the return value of the await call?

For example, are there any differences in the way these 4 files are created? because if I run this, all 4 of them are created, so I wonder.

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');
})()

Update:

I tried some tests and it looks like it behaves as answered, but in the end, things got weird.

So if you run the following,

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

output is sometimes foo and sometimes bar, which seems like both writeFile calls are fired at the same time and who finishes last depends on luck.

so I changed it to:

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

and I thought (based on the answers) that this would always output bar, but output again is sometimes foo and sometimes bar.
I'm not sure I understand, because I thought I awaited the call that writes bar, so the only explanation would be that the first writeFile call somehow gets its turn after the awaited call.

then I tied to put a delay between the calls:

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

and this time it always outputs bar.

then something weird happened.
if you run the following:

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

the output is boo, which the only explanation I can think of is that the first writeFile writes foo to the file, then second writeFile starts to write to the file but before it's finished doing it, the awaited readFile reads the file. (LOL)

Upvotes: 0

Views: 92

Answers (3)

Jacob
Jacob

Reputation: 78850

In this sample code:

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

The first writeFile may complete at any time since it's not being awaited. It doesn't matter if you're waiting for bar to be written; that just means your read completes after the second write. The first write may complete after that still. That's why readFile could be either foo or bar.

Maybe it'd help to visualize by showing the equivalent code not expressed as an async function:

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);
    });
}

This is a so-called race-condition. Think of parallel running tracks:

                  File state

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

Because we're not waiting for that first write, it may complete at points A, B, C, D, or never (if it fails or takes forever).

  • If it completes at point A, then 'bar' will overwrite 'foo'
  • If it completes at point B, then 'foo' will overwrite 'bar'
  • If it completes in the middle of the read at point C, you may even read a partial write!

The only way to guarantee a write order is to await to keep them in order:

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

For this block of code:

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

...you are correct that since you are not awaiting writeFile in any case, readFile may be reading the file in the middle of it being written.

Upvotes: 1

Alex Vovchuk
Alex Vovchuk

Reputation: 2926

await is required in this case if you are using result value OR if you have to wait until function under await being executed.

await stops main thread execution till function under await returns.

Upvotes: 0

Jonas Wilms
Jonas Wilms

Reputation: 138257

It just doesn't await the result then. It will just fire & forget the asynchronous task. Thats not what you want in most cases, as code runs out of order and errors aren't handled.

 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.

Upvotes: 2

Related Questions