Is there a way to make it work without changing the app.js file?
In Javascript, you cannot make asynchronous results work synchronously. Can't do it. There are various tools such as promises and async/await
that help you program with asynchronous operations, but the fundamental rule is that an asynchronous results is always going to be asynchronous. The only wait to run code after it is to hook into the asynchronous result and only execute that code when you get the notification that the async result is now done.
Within an async
function, you can use await
to write "synchronous looking" code for asynchronous operations, but it only "looks" synchronous and it only applies within that function, it's not really synchronous (see explanation below).
I was expecting working to appear between the begin and end lines.
The usual misunderstanding here is that an async
function is NOT blocking. It doesn't wait until all await
operations inside it are done and completed before returning.
Instead, it runs synchronously until the first await
. At the point where it gets the first await
, it returns a promise back to the caller and the code after that function call continues to run.
Sometime later, when the promise resolves that was awaited and no other Javascript is running at the time, then the function picks up where it left off and runs some more until the next await
or until it returns. If it finds another await
, then it again pauses execution and returns control back to the JS interpreter to run other events (non-blocking). If it gets to the end of the function block or encounters a return statement, then the promise that it returned earlier gets resolved.
So, in this code of yours:
const service = require('./service');
console.log('*********** begin ***********');
(async () => {
const results = await service.init();
})();
console.log('*********** end ***********');
Here's the sequence of events:
- Load the service module synchronously.
console.log('*********** begin ***********');
- Call the async IIFE function.
- That async function runs until it gets to the first
await
. service.init()
runs and returns a promise which the await
operation is going to wait for. At that point, that function returns a separate promise (which you are not using or paying attention to). All async
functions return a promise.
- The code following that async function runs now and you get
console.log('*********** end ***********');
.
- Sometime later
service.init()
resolves the promise that it returned and the results
variable is filled with the resolved value of that promise.
While async
and await
can be enourmously useful, they are mostly syntactic sugar that just makes programming easier. They can be transpiled into regular promise handling that uses .then()
instead of await
. For example, suppose you had this:
async function foo1() {
const results = await service.init();
console.log("got results", results);
return results;
}
foo1().then(results => {
console.log("all done now");
}).catch(err => {
console.log(err);
});
That foo function could also be written like this:
function foo2() {
try {
return service.init().then(results => {
console.log("got results", results);
return results;
});
} catch(e) {
return Promise.reject(e);
}
}
foo2().then(results => {
console.log("all done now");
}).catch(err => {
console.log(err);
});
These two implementations behave identically. The try/catch
in foo2()
is something that the async
function does automatically and is only useful if service.init()
or anything else inside of foo2()
might throw an exception synchronously.