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.

  • readFileSync is blocking, whereas await readFile yields control so that other things can be happening while you wait. See e.g. stackoverflow.com/questions/47939680/… – jonrsharpe Dec 30 '18 at 15:44
  • Yes but what "other things"? Can you confirm it's the two kinds of things I mentioned (i.e. other awaiting code, and code scheduled using setTimeout)? And can you confirm that there are therefore potential consistency problems as if the code were multi-threaded, in spite of the fact that tutorials say that JavaScript is single-threaded? – ChrisW Dec 30 '18 at 15:48

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 awaiting 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 calls await might therefore be reentered. – ChrisW Dec 30 '18 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 Dec 30 '18 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 Dec 30 '18 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/immediate – Hero Wanders Dec 30 '18 at 16:53

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.