Sorry for this naive question.
Reading some existing JavaScript I see a function with code like ...
async function _load() {
await ensureDir(path);
return await readFile(filename);
}
... why not use readFileSync
instead, what's the difference?
When you call await (e.g. in Node.js) does other code become runnable, including e.g.:
- Other code which went to sleep by calling
await
- Code scheduled using e.g. setTimeout
If so I guess you must worry about the same kind of thing you worry about in multi-threading, e.g. ...
- Updating state might require writing to several files
- You might await after writing one file
- Another "thread" might wake up, read the files, and find them in an inconsistent / partially-updated state
... is that so?
The difference being only or mainly that, unlike with threads, there are limited places where a thread-switch might occur -- i.e. only when you explicitly call await
.
I have tried Google for an answer. The tutorials I've found tell me what happens to my code when it calls await but doesn't tell me (doesn't confirm) explicitly what other code might become runnable when it awaits. I've read that "there's a JavaScript event loop", and that, "JavaScript is single-threaded", but I don't know how await
fits into that. The above is my guess as to how it works, I hope you can confirm my guess is correct, or correct it, or reference a tutorial which touches on this topic.
1 Answer 1
The JavaScript runtime has to perform many tasks. This includes handling mouse/keyboard events, running code scheduled with setTimeout
, handling responses of asynchronous web or other IO requests etc.
Working off all those tasks is done sequentially in the event loop.
When starting an async function (which returns a promise), applying await
to it essentially suspends the execution of the current task (you may also think of it as having all further code to be wrapped in a then
handler).
This allows the engine to immediately perform other tasks which are next in the event loop. When the awaited async result becomes available, the suspended task is queued again to continue its work.
Indeed you have to expect that the state of the program has changed considerably after await
ing an async result as other tasks may have run in the meantime. You should consider your code before and after the await
as two separate tasks, just as you should when using then
on promises.
Nevertheless the runtime guarantees that no two tasks will run in parallel.
-
So if the process is receiving and handling (i,e. processing) a sequence of network messages, for example, then calling
await
during the processing of a message lets the scheduler (the JS event loop) begin to process the next message while this one is awaiting (which is more responsive); and the cost (the down-side) is that the module which callsawait
might therefore be reentered.ChrisW– ChrisW2018年12月30日 16:17:33 +00:00Commented Dec 30, 2018 at 16:17 -
A module which wants to await but which can't afford to be reentered might implement a private queue queue of pending/promised events which it can process one by one (i.e. the module's public methods enqueue events, the module's implementation dequeues the first event and awaits for it to complete before dequeuing the next). Is that so?ChrisW– ChrisW2018年12月30日 16:18:16 +00:00Commented Dec 30, 2018 at 16:18
-
And maybe that queue is up near the top, i.e. network event handlers simply enqueue each received message for processing, and the process dequeues and processes them one by one. In which case there's no benefit to using
await
(or is there?) except that perhaps you can have other code run using setTimeout which behaves as if it ticks at interrupt level (i.e. when it's allowed to run while the main message-processing loop is awaiting).ChrisW– ChrisW2018年12月30日 16:47:03 +00:00Commented Dec 30, 2018 at 16:47 -
There is a special microtask queue which contains callbacks executed immediately after the code which has been run in the current task without the penalty of starting a new task: jakearchibald.com/2015/tasks-microtasks-queues-and-schedules I'm not sure how the
await
keyword behaves here. Apart from that you may build your own microtask queue which prioritizes the callbacks as you like or use a library like immediate: github.com/calvinmetcalf/immediateHero Wanders– Hero Wanders2018年12月30日 16:53:32 +00:00Commented Dec 30, 2018 at 16:53
readFileSync
is blocking, whereasawait readFile
yields control so that other things can be happening while you wait. See e.g. stackoverflow.com/questions/47939680/…