Can I use a async function as a callback in node.js? - javascript

Can I use an async function as a callback? Something like this:
await sequelize.transaction(async function (t1) {
_.each(data, async function (value) {
await DoWork(value);
});
});
//Only after every "DoWork" is done?
doMoreWork();
As far as I understand there is no guarantee that the function invoking the callback will wait until the promise is solved before continuing. Right? The only way to be sure what will happen is to read the source code of the function the callback is passed to(e.g. source code of 'transaction')? Is there a good way to rewrite my sample to work properly no matter how the calling function is implemented?

async function can be as a callback but only if returned value (a promise) is used in some way that helps to maintain correct control flow. An example is array map callback.
This is the same case as this problem with forEach. The problem is that transaction uses a value from async callback (a promise) but a value from each callback is ignored.
The recipe for executing promises in series with async is for..of or other loop statement:
await sequelize.transaction(async function (t1) {
for (const value of data)
await DoWork(value);
});
The recipe for executing promises in parallel with async is Promise.all with map:
await sequelize.transaction(async function (t1) {
await Promise.all(data.map(async (value) => {
await DoWork(value);
}));
});
async functions are left for reference only because the code doesn't benefit from them; these could be regular functions that return promises.

Related

how does this async/await work when it's not being called in async function

I have a promise stored in a const
const goToWork = new Promise((resolve, reject) => {
setTimeout(() => resolve('Go to Work'));
});
since calling goToWork() would error expression is not callable
I tried storing running it in an async function:
async function callPromise() {
return await goToWork;
}
now awaiting callPromise as such await callPromise() returns pending<Promise> which tempted me to store it in another async function as such:
async function executePromise() {
console.log(await callPromise());
}
and then ran it executePromise()
// output: Go To Work
my question is how come executePromise() didn't complain of not running in an async function ? how come it did not need to be "awaited"
I tried storing running it in an async function:
There really is no point at all this this function:
async function callPromise() {
return await goToWork;
}
It absolutely the same as:
function callPromise() {
return goToWork;
}
which is a pointless function that doesn't do anything useful. So, since you're making a function with no practical use, there must be some basic misunderstanding of async/await.
To start with, all async function return a promise - always. So, return await fn() is not useful. You may as well just do return fn(). Both return a promise with the same state.
now awaiting callPromise as such await callPromise() returns pending which tempted me to store it in another async function as such:
Yes, as I said above. All async functions return a promise.
and then ran it executePromise() // output: Go To Work
There should be no surprise here.
console.log(await callPromise());
This will output the result of await callPromise() which (if it doesn't reject) will output the resolved value of the promise that callPromise() returns.
my question is how come executePromise() didn't complain of not running in an async function ? how come it did not need to be "awaited"
Here's your executePromise function:
async function executePromise() {
console.log(await callPromise());
}
There's no problem with this function because the await is inside an async function. That follows the async/await rules.
When you call it like this:
executePromise()
That just runs the function and because that function is an async function, it will always return a promise. There is no rule that calling an async function requires using await on it or must be called from within another async function.
You can call it like this:
executePromise().then(...).catch(...)
Or, you can put it in a async function:
async someFunction() {
try {
await executePromise();
} catch(e) {
// got error
console.log(e);
}
}
Or, you can just call it without regard for the returned promise:
executePromise();
Calling it naked like this last one is not paying any attention to whether the returned promise resolves or rejects and is not paying any attention to any resolved value. But, it's legal to do this. It possibly sets you up for an unresolved rejection because there's no reject handler, but if you know that promise will never reject and you don't care when it resolves or what the resolved value is, then this is allowed.
my question is how come executePromise() didn't complain of not running in an async function?
But it did run in an async function: async function executePromise().
Keep in mind that await is syntactic sugar (more or less, see comments), and you can always turn it back into Promise.then(), where this:
const x = await Promise.resolve('foo')
console.log(x)
behaves similar to
Promise.resolve('foo').then(x => console.log(x))
This makes it easy to understand what is going on:
async function callPromise() {
return await goToWork;
}
is similar to
async function callPromise() {
return goToWork.then(x => x);
}
And
console.log(await callPromise());
can be thought of as
goToWork.then(x => x).then(x => console.log(x))
None of that needs to be awaited, but you can use await to make it more readable.

Designing function callbacks with Async calls

What is the best practice when designing code that can work with async functions inside callback functions, here is a simplified view of the problem.
There is this update functions that makes some async function calls to update some data to some kind of storage.
function update(){
asyncfn(arg, () => {// Callback code});
}
then there are these two buttons to call the update function
Updatebtn.onClick = update;
UpdateDisplaybtn.onClick = () => {
update();
displayUpdatedData();
}
The second button will cause problems as the displayUpdatedData() will be called before the update() is truly finished as it has an async function call that will not be finished, also i can not specify my own callback in update() as it is used as a callback to the onClick event.
I don't know much about promises but what i know that the async functions i use does not support them.
I am not looking for some kind of workaround, i am looking for the best practice in a situation like this.
UPDATE
After learning that the best practice is using promises i used this YouTube playlist "JavaScript Promises" by The Coding Train to learn about them which was very good and i wanted to share it with anyone who want to learn about Promises.
If you're working with a function that only accepts a call back, you can wrap it inside of a promise so you can leverage async / await. If the function already returns a promise, you can just use the async / await without having to wrap it.
Doing it this way, you can still have your sequential execution of your update and displayUpdatedData functions by simply putting them within an async function and awaiting them.
function callbackFunction (cb) {
// ... stuff is happening
cb('Data from the callback func');
}
async function update() {
return new Promise( (resolve, reject) => {
callbackFunction( (data) => {
resolve(data); // or reject(); if failure
});
});
}
const doThings = async () => {
const result = await update();
console.log(result);
// TODO: Display data from the result
};
doThings();
So if you are looking for best practices, I highly recommend you familiarize yourself with promises. Javascript now supports the async / await syntax, which allows you to await the response of promises, but lets you write your code more or less like an ordinary function. I know you are saying that the async functions you want to use require callbacks, and do not support promises, but you can convert a callback based function to a promise based one (promisify) using libraries like bluebird, or if you are in node, it has a native promisify function of its own.
So then you would end up with something like this:
import cbFn from 'cbFn'; //import or require your callback based function
import {promisify} from 'util';
const pFn = promisify(cbFn);
async function update() {
await pFn()
}
...
UpdateDisplaybtn.onClick = async () => {
await update();
await displayUpdatedData() //await only needed if displayUpdatedData is also async / a promise
}

Why Does Async Always Return a Promise?

This question is theoretical - I have no concrete problem to solve.
With that said, why does the async keyword wrap the return value of an async function in a promise? What's the point? Is it ONLY because the await expression expects a promise? Or is there some meaning / use behind this decision?
I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.
You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.
The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)
Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!
The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.
The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below
let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)
When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it
if however
We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.
When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}
container()
You can use await and .then interchangably
For example we could go:
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}
container()
once again the code will pause at .then and then continue when the asyncEvent promise has resolved.
In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
asyncEvent.then(() => console.log(scopedVariable))
The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.
In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.
I hope this helps!
The async and await operators are just syntactic sugar that hide the underlying use of promises to implement asynchronous code.
Using async before a function definition makes the function return a promise that resolves to the function's return value, rather than returning normally.
Using await before an asynchronous function call suspends the current function until the promise that it returns is resolved. It's basically equivalent to wrapping the remainder of the function in an anonymous function, and using that as the .then() callback of the promise.
For more information between the relationship, see How to translate Promise code to async await

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.

Can I use async await without .then?

I'm a bit confused about the MDN documentation of async await. These docs use the .then() syntax to respond when an async function has resolved:
mdn example
async function add() {
const b = await resolveAfter2Seconds(30); // timer
return b;
}
add().then(v => {
console.log(v);
});
But in my own code, I don't use .then() and it still works asynchronously. The flow is not blocked. So why would you use .then() ?
async code without .then
function start(){
console.log("starting!")
let d = loadData()
console.log("this message logs before loadData returns!")
console.log(d) // this shows: Promise - Pending because d has not yet returned
}
async function loadData() {
const response = await fetch("https://swapi.co/api/films/");
const json = await response.json();
console.log("data loaded!")
return json;
}
First of all, all async functions return a Promise, so if you want to get the returned value from that async operation, you'll either need to use then or await inside an async function.
MDN uses .then because the add async function is being called outside an async function scope, so it can't use await globally to catch the data from the promise.
In your example, you get the same, an instance of Promise as the return of your loadData async function, if you define the start function also as async, you can use let d = await loadData(), if it's not async, you can use .then (which is the Promise API).
Async function declaration async function loadData() returns AsyncFunction which is executed asynchronously and always returns a Promise.
Basically all the code you put inside the async function someFunctionName will be executed inside that Promise and when you return some value inside that function – it will resolve that promise.
So that then() call is to get the actual value from that Promise that your function returned.
Your code works because it is not returning promise object and actually waiting for the response. It returns the json at the end. So, execution holds till it gets the response.
If your function is async you don't need to return promise, your return statement will wait for all 'await' statement before it to complete.
Your own code works, because you replaced the usage of .then() with the usage of async await.

Categories