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.