async await utility function synchronization? - javascript

If we define a utility function like this:
/**
* Delete the entire database
*/
export async function deleteDatabase(db) {
await db.delete()
}
And we call it.
deleteDatabase(db)
nextFunction()
Then next function will not be called before the db.delete() has completed correct?
I think this is how it works, so I just want to confirm the semantics.
DexieJS Demo
Incorporating the answers into this DexieJS Demo for those interested in the actual use case:
https://stackblitz.com/edit/typescript-dexie-one-to-many

"Then next function will not be called before the db.delete() has completed correct?" Wrong. It will be called BEFORE async operation has completed.
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
async function deleteDatabase() {
console.log('Deleting DB')
await delay(1000)
console.log('DB deleted')
}
function nextFunction() {
console.log('Next function')
}
deleteDatabase()
nextFunction()
You need to have another async/await wrapper function or use .then to chain promises
deleteDatabase().then(nextFunction)

deleteDatabase(db)
This expression returns with promise immediately.
So nextFunction() will be called before the db.delete() has completed
You should await for deleteDatabase(db) as well

If they're in a async context, the next function won't be called, only if you put the await before the deleteDatabase
Like this:
async myFunc(db) {
await deleteDatabase(db)
nextFunction()
}
Or even:
async myFunc(db) {
return deleteDatabase(db)
.then(() =>
nextFunction()
)
}

If you call it like
deleteDatabase(db)
nextFunction()
The next function will be called immediately as you are not awaiting it.
You need to await
await deleteDatabase(db)
nextFunction()

Related

Does NestJS guarantee to run async methods after return?

What will happen when a code without await an async function returns from a REST API of NestJS?
// modified from https://stackoverflow.com/questions/59829254
#Post()
async create(#Body() body: Dto, #Headers('id') id: string) {
body.id = id;
const item = await this.service.create(body);
this.service.anotherAsyncMethod().then(...).catch(...);
return item;
}
In this example, does the process always live to run the complete anotherAsyncMethod?
If it does, how can it be? Do NestJS or Javascript itself guarantee the way to execute?
It seems there are some waitings in simple test code.
async function asyncFunction() {
setTimeout(() => console.log("after 3s"), 3000);
}
asyncFunction();
console.log('END');
// output
// END
// after 3s
But I could not find the clue that it always work in NestJS scenario and the others.
In JavaScript, any piece of code that comes before the return statement will be executed. Do not forget that the await statement will only wait for the result, and if you put await before an async operation, it will wait for its result, but even if you did not put await, the async operation still placed in the event loop and will still be executed.
The only principle is that all these operations should be before the return statement. The return means termination of that function. Not only in JavaScript but in all languages, nothing will be executed after return.
function someOperations(status) {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(status)
res('done');
}, 3000);
});
}
(async () => {
console.log('start...');
await someOperations('first');
someOperations('second');
console.log('second someOperations skipped for now');
return;
console.log('unreachable'); // will not be executed
})()

await not doing what I expect

async function doThings() {
async function timer () {
setTimeout(() => {
console.log('timer!')
}), 1000
}
async function printer () {
console.log('printer!')
}
await timer()
await printer()
}
doThings()
I thought making the function async made it return a promise and await made it wait. Why does printer() finish first?
Your timer function doesn't work because setTimeout does not return a promise that could be awaited. You will need to promisify it manually:
// set a default of 1500
function timeout(ms=1500) {
return new Promise(resolve => setTimeout(resolve, ms));
};
async function printer() {
console.log('printer!');
};
async function doThings() {
await timeout(1000); // time in ms
await printer();
};
doThings();
The word “async” before a function means one simple thing: a function always returns a Promise. Other values are wrapped in a resolved Promise automatically.
Now when we use don't return Promise from async function, then javascript interpreter automatically returns resolved Promise, so that is the reason await on timer function got the resolved Promise and interpreter went ahead with the execution, but there is a setTimeout still going on. So we have to return a Promise from timer function, and resolve it inside setTimeout function like below.
async function doThings() {
async function timer() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`timer!`);
resolve();
}, 1000)
});
}
async function printer() {
console.log('printer!')
}
await timer()
await printer()
}
doThings()
All I can see is timer () and printer () functions are async functions but there is no await used in them, and this makes the async keyword unfunctional.

Javascript await a function inside an async function

I have the following codes:
function test1() {
test2.execute();
}
I am trying to use await inside an async function to make test1() execute first:
it("title", async () => {
await test1();
await firebase.assertSucceeds(true);
})
But somehow the function test1() does not execute before firebase.assertSucceeds(true). test1() returns a callback. I tried to use the callback, and it does not help either.
function test1() {
test2.execute().then(() => {
return;
});
}
it("title", async () => {
let x = await test1();
await firebase.assertSucceeds(true);
})
Still synchronous. what did I do wrong here?
In your first snippet you are awaiting for test1 which returns void (nothing), for the await to work it should be applyed to a promise, you acheive that either by returning test2.execute() which returns a Promise :
function test1() {
return test2.execute();
}
or by making test1 async and await for test2 to finish executing :
async function test1() {
await test2.execute();
}
ps: an async function returns a resolved promise with the return as the resolution value so for example :
async function test(){
return 2;
}
is translated under the hood to this:
function test(){
return Promise.resolve(2);
}
A function has to return a Promise if you want to await it.
For example:
function test1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("I'm done")
}, 500)
})
}
Notice, in the firebase documentation, that assertSucceeds only takes a promise as it's one and only argument. So consider passing it a promise that always resolves if that's what you're going for to begin with.
await firebase.assertSucceeds(Promise.resolve())

Writing async/await version of a promise

The code below logs 'hello world' once in a second as expected.
function moveOneStep() {
return new Promise((res, rej) => {
setTimeout(() => {
res(console.log('Hello world!'))
}, 1000)
})
}
async function main() {
await moveOneStep();
await moveOneStep();
await moveOneStep();
await moveOneStep();
}
Considering the return value of an async function corresponds to what is returned from resolve function in promises, why doesn't the code below output the same result, but instead logs all the 'hello world's at once:
async function moveOneStepAsync() {
setTimeout(() => {
return console.log('Hello world!');
}, 1000);
}
async function main() {
await moveOneStepAsync();
await moveOneStepAsync();
await moveOneStepAsync();
await moveOneStepAsync();
}
That's because setTimeout does not return promise to await it in your main function. setTimeout itself executes synchronously. It adds to an event loop the callback that is passed as an argument to execute in time that is mentioned.
Also in this code return of your callback means nothing as callback with be run in 1 sec and the returned value will go nowhere.
async keyword tells you that functions returns promise and could have awaitable code in it. So as there is not await in your code it then looks like
function moveOneStepAsync() {
setTimeout(() => {
return console.log('Hello world!');
}, 1000);
return Promise.resolve();
}
So your await in main will await one event loop tick to go to the next "step"
Read about setTimeout, event loop, and what await expects to understand it more in-depth
You do not return anything to the function, you return back to the internal setTimeout function, and that does not do anything with that value. Calling setTimeout does not return a promise, it rather returns a timer immeadiately. And therefore all the moveOneStepAsync calls execute immeadiately, the timers get set, and fire after one second.

How to use await in a callback?

My code is:
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text);
await sleep(5);
});
}
run();
await sleep(1) is fine, but await sleep(5) results in:
SyntaxError: async is only valid in async function
Is fs.readFile the problem? How do I use await in such a situation?
The above is a reduced example to test. In my actual usage, I need to put the await sleep in a callback deeply nested inside a few more asynchronous functions.
I'm not sure what sleep is doing, but the problem in your question is happening because the callback to fs.readfile is not an async function and the that is where await sleep(5) is.
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text); /* ^this isn't an async function */
await sleep(5);
});
}
You could make that error go away by passing an async function as a callback, however mixing promise and callback flows is not recommended and leads to problems, especially when handling errors. It prevents you from chaining the promise and catching errors from the inner await outside of run().
A better approach is to wrap fs.readFile() in a promise and await that.
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
This will allow you to catch any errors in much more robust way with:
run()
.then(() => /*...*/)
.catch(err => /* handle error */
and avoid unhandled rejections.
The above is a reduced example to test. In my actual usage, I need to put the await sleep in a callback deeply nested inside a few more asynchronous functions.
Callback based APIs shouldn't be augmented with async..await or promises in general when there is a chance to stick to promises (may not be possible if a callback is called more than once). This results in poor control flow and error handling.
Once there is async callback function inside run, it's impossible to chain nested promise with run().then(...). Errors from nested promises may remain unhandled as well and result in UnhandledPromiseRejectionWarning.
The proper way is to move from callbacks to promises and use promise control flow. Generally this can be achieved with promise constructor:
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
There are numerous ways to get promises from Node callback based APIs, e.g. fs provides promise API since Node 10:
async function run() {
await sleep(1);
const text = await fs.promises.readFile('list.txt', 'utf8');
console.log(text);
await sleep(5);
}
If your function includes an "await", you must prefix your function declaration with "async".
NOTE: By making your function an "async" function a promise is always returned. So if you return a string, that string will get automatically wrapped in a Promise object.
You can therefore return a Promise manually (for clarity). A simple resolved promise is sufficient:
async function foo(){
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
}
The keyword "async" always goes before the keyword "function":
var foo = async function(){
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
}
... and well for arrow functions:
var foo = sally( async () => {
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
})
To retrieve the return value:
foo().then(val => console.log(val) ); // prints "optional" to console
You have to use aysnc key before function start. await keyword
will not work with normal function.
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', async function (err, text) {
console.log(text);
await sleep(5);
});
}
run();

Categories

Resources