Reputation: 55
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');
})()
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
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).
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
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
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