Async/Await Nuances - javascript

I am trying to understand some of the nuances of async/await in Javascript and was hoping I can get some help here.
Suppose I have the following functions:
async function sum(a, b) {
return await adder(a, b);
}
function sum(a, b) {
return adder(a, b);
}
It's my understanding that the return values of the above functions are the same, due to async returning a promise, or wrapping non-promises.
Suppose adder returns a number. The first function will first resolve the await adder which will be a number, but then the async will wrap it as a Promise<number>.
Similarly, for the second one, a regular Promise<number> will be returned.
Am I correct in saying this?

It's my understanding that the return values of the above functions are the same...
Not quite, no, but you seem to know that:
Suppose adder returns a number. The first function will first resolve the await adder which will be a number, but then the async will wrap it as a Promise<number>.
Similarly, for the second one, a regular Promise<number> will be returned.
No, not if adder returns a number. The second one (the non-async sum) will return a number, not a Promise<number>, because neither it nor adder is async.
If adder returns a native Promise<number>, then the return values of async function sum and function sum are the same, although there may be a slight difference in timing.
There's never any real reason to use return await x unless you have it wrapped in a try/catch and you want to handle any error from x locally in the function. Otherwise, just return is sufficient, even if what you're returning is a promise. Until the ES2019 spec, return await somePromise and return somePromise were handled slightly differently, the settlement of the first was delayed one async tick longer than the settlement of the second, but if it's a native promise, a change in the ES2019 spec makes them the same even at that level. (But if somePromise were a non-native thenable, the extra tick would remain.)

Please refer to the inline comment for details.
async function sum(a, b) {
return a + b;
}
const promise = sum(1, 2);
// Here calling sum will return promise.
// Whatever you return it will be wrraped in promise[for simplicity]
// Above code is similar to
function sum1(a, b) {
return Promise.resolve(a + b);
}
// Assuming adder funtion is async function
function adder(a, b) {
return new Promise((r) => {
setTimeout(r, 1000, a + b);
});
}
// So calling adder
async function sum2(a, b) {
return await adder(a, b);
}
// Equivalent to
async function sum3(a, b) {
const data = await adder(a, b);
return Promise.resolve(data);
}
// sum3 will return promise
sum3(1, 2).then(console.log);
// which is equivalent to
async function sum4(a, b) {
return adder(a, b);
}
// sum4 will return promise
sum3(1, 2).then(console.log);

async functions always return promises.
so assuming adder is async function both of the functions return promises.
in function 1 it will wait for the promise returned from adder to be resolved, once resolved it will return the number value, but async function always wraps the return value in promise if it isn't the in first place. so it returns a promise.
in function 2 it is because it returns the return value of adder which is a promise;
think of the return clause in async function as resolving the promise it returns.

Related

Javascript - what is the difference between Promise.all vs just iterating when doing async operation inside? [duplicate]

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

JavaScript is not waiting for Async Await [duplicate]

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

setTimeout with Promises

I trying to solve a challenge with the following question:
Implements a function that takes a number as a parameter and after x milliseconds (between an interval of 1 to 100 ms. Use setTimeout and as floor and random functions from the Math library), without a console or with twice the received parameter. Then, call this function 5 times. Ex.:
Without performing any treatment, it is easy to notice that the order of the values ​​shown on the console is random and does not accept the order of calling the functions. Therefore, to resolve this issue, treat or subscribe to setTimeout using callback, Promise and async / waitit.
That's the expected behavior:
let result;
result = double(5, 0); // returns 10
result = double(12, result); // returns 34
result = double(2, result); // returns 38
The goal is to treat the asynchronous behavior of setTimeout with promises, async functions or callbacks.
That's was what i got until now and with no success:
function promisify(number, increase){
return new Promise(resolve => setTimeout(() => resolve(number * 2 + increase), 100))
}
async function double(number, increase) {
const value = await promisify(number, increase);
return value;
}
let result;
result = double(5, 0)
result = double(10, result)
result = double(20, result)
console.log(result)
I want to return a promise with a set timeout that was a random calculate miliseconds until resolve the double of the number + the increase value if it exists.
Even waiting for the promise to result in the async function, it continues to return a pending promise
The result variables have to increase on each calc but they're receiving functions instead of the double result
You're actually almost there. All you need is to assign the value of the resolved promise to result, instead of assigning the Promise object directly. This is done by using result = await double(<param1>, <param2>).
However, since JS does not yet support top-level await, you need to wrap your whole result assignment logic in another async function, and then call it as such:
function promisify(number, increase){
return new Promise(resolve => setTimeout(() => resolve(number * 2 + increase), 100))
}
async function double(number, increase) {
const value = await promisify(number, increase);
return value;
}
async function run() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
}
run();
However, looking at your code, the double function seems to only serve as a wrapper. Your code can be easily rewritten so that you perform the calculation immediately, but simply wait a little before resolving the promise:
// Simply forces the async operation to wait for a set duration
function wait(duration){
return new Promise(resolve => setTimeout(resolve, duration));
}
async function double(number, increase) {
await wait(100);
return (number * 2) + increase;
}
async function run() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
}
run();
In this way, you can then implement the question's desired randomized settimeout, if you want to, i.e.:
// Waits between [1, 1000] milliseconds
await wait(Math.random() * 1000);
You are close to the solution. The reason why this does not work is the following:
double is an async function. That means it does not return 10, 20, or any other number, but a Promise that resolves to this number as soon as possible (in this case, after the timeout).
That means you should wrap your code into another async function and use await to handle the promises:
async function doPrint() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
return result;
}
doPrint().then(function(result) { console.log('Returned result ' + result); });
Notice that the "then" method represents another way to deal with async functions: It is the usual method of any promise. Just remember that any async function returns a promise under the hood (even when not explicitly specified). The await syntax is just syntactic sugar around dealing with the then calls.

Are async functions really functions or Promise objects?

I was just trying different combinations of async await problem statements and I tried this,
basically I have two functions promise1() and promise2() which return two promises which resolve values 10 and 20 after 3 and 1 seconds respectively. Below is the code that I have written
function promise1()
{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(10)
},3000)
})
}
function promise2()
{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(20)
},1000)
})
}
async function sum()
{
let num1 = await promise1();
let num2 = await promise2();
return num1+num2;
}
sum().then(s => console.log(s))
when I execute this code I am getting 30 as answer
as in the above code if I was able to apply .then() to sum then is it a promise?
when I did console.log(typeof sum) its saying sum is a function not an object so what exactly are async functions? I have tried searching the answer for this behaviour but couldn't find any sources that would correctly answer. It would be a great help for me if someone answered this query or tell me the online sources or books that would get me the answer
my node version is v10.15.3
thank you
They are functions where the return value is always a promise.
async function foo() { }
const value = foo();
console.log(foo instanceof Function);
console.log(foo instanceof Promise);
console.log(value instanceof Function);
console.log(value instanceof Promise);
as in the above code if I was able to apply .then() to sum then is it a promise?
No, you weren't.
sum.then() won't work. sum().then() will. Adding () will call a function and give you the return value.
Are async functions really functions or Promise objects?
They're really functions. The functions return promises.
as in the above code if I was able to apply .then() to sum then is it a promise?
You didn't. You used .then() on the result of calling sum (sum().then(...), not sum.then(...)). It's just like calling a function that returns a string, then using toUpperCase on what the function returns.
These two functions are essentially equivalent, handwaving some minor details:
// #1
function foo() {
return new Promise((resolve, reject) => {
try {
getSomePromise()
.then(result => result * 2)
.catch(reject);
} catch (error) {
reject(error);
}
});
}
and
// #2
async function foo() {
const result = await getSomePromise();
return result * 2;
}
Naturally, that's not how you'd probably write #1 in real life, it's an example of the promise creation antipattern, but it's a reasonable interpretation of what an async function looks like under the hood. Purely for completeness, you'd probably write #1 like this:
// #3
function foo() {
return getSomePromise().then(result => result * 2);
}
The difference is that if getSomePromise throws an error rather than running and returning a promise, that function (#3) will throw an error (synchronously), whereas #1 above will return a promise that rejects instead. async functions do the latter, which is why #1 is written the way it is above.

map() function with async/await

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

Categories

Resources