Async/Await - code executes before promise is resolved [duplicate] - javascript

This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 2 years ago.
I have an async function that processes an Array and call another async function by increasing time interval by each element.
I wait for all promises to resolve and then I save the resulting Array in a file. Though for some reasons the write file operation executes before the Promises are resolved.
Can somebody tell me what I might be doing wrong?
The read and write file functions are turned into promise with node util and the getGuidesList returns a promise.
(async () => {
try {
const file = await readFilePromise('./kommuner.json');
const municipalities = JSON.parse(file).municipalities;
console.log(municipalities);
const municipalities_new = await Promise.all(municipalities.map(async (municipality, index) => {
setTimeout(async () => {
let guides = await getGuidesList(municipality.municipality_id);
// const guides = [];
if (typeof guides === 'string') {
guides = [];
}
console.log(`Number of guides for ${municipality.municipality_name}: ${guides.length}`);
Reflect.set(municipality, 'guides_number', guides.length);
Reflect.set(municipality, 'guides', guides);
console.log(municipality)
}, index * 5000);
}))
console.log(municipalities_new);
await writeFilePromise('./kommuner_guide.json', JSON.stringify({
"municipalities": municipalities_new
}));
} catch (err) {
console.log(err);
}
})();

The problem here is this line:
setTimeout(async () => {
you do a setTimeout call. That will schedule the callback to be called later. But you don't wait for that callback to happen. Instead use some promisified version of setTimeout:
const timer = ms => new Promise(res => setTimeout(res, ms));
then you can
await timer(2000);
await /*other stuff*/;

Related

What's the difference between await an async function and directly calling an async function [duplicate]

This question already has answers here:
Why do I need to await an async function when it is not supposedly returning a Promise?
(3 answers)
Closed 1 year ago.
I recently read a book about async and await patterns, there's chapter about deferred await or early init. The book says:
This allows a structure that starts async processing early but only stops for it when the result is needed.
const wait = (ms) => new Promise(res => setTimeout(res, ms));
const fn = async () => {
console.log("starting");
await wait(100); // I suppose await should pause the execution
console.log("end");
}
(async () => {
const fnPromise = fn();
console.log("after"); // However after is printed brfore end
await fnPromise;
})();
And if I change the code a bit
(async () => {
await fn(); // looks like the inner await only takes effect when I await an async function
console.log("after"); // after is printed after end
})();
I mean what's the difference between await an async function and directly calling it. Is there any best pratice about when to await or when not to await an async function. Does await truly block the execution, especially combined with timers.
Thanks to #asynts's snippet, I'll post it here
let samples = 100 * 1000 * 1000;
let chunk = 100000;
async function run() {
let sum = 0.0;
for(let i=0; i<samples; i++) {
sum += Math.random();
if (i % chunk === 0) {
console.log("finished chunk")
// wait for the next tick
await new Promise(res => setTimeout(res, 0));
// If await truly blocks the execution, rest of the code are all waiting?
// If await doesn't block the execution, what's the point to await here
}
}
let mean = sum / samples;
console.log("finished computation", mean);
}
setTimeout(run, 0);
What's the difference between await an async function and directly calling it.
Well, you already observed the difference clearly in your first two snippets.
Is there any best practice about when to await or when not to await an async function?
Yes: always do it immediately. Do not let promises sit around un-awaited (like your fnPromise variable).
Not doing so is not necessarily wrong, but it increases the likelihood of mistakes:
it makes the order of execution less obvious and the code harder to understand
it can cause rejections to be missed (not handled) while doing something else (e.g. waiting for something else, or even conditionally breaking from a loop etc.) which may cause your program to crash. See Waiting for more than one concurrent await operation or Any difference between await Promise.all() and multiple await?

Best way to to use async/promise/callbacks in for loop [duplicate]

This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 2 years ago.
I'm building a trading bot that needs to get stock names from separate files. But even I have used async function and await in my code, that doesn't work.
My index file init method.
const init = async () => {
const symbols = await getDownTrendingStock();
console.log("All stocks that are down: " + symbols);
const doOrder = async () => {
//do stuff
}
doOrder();
}
my getDownTrendeingStock file
const downStocks = []
function getDownTrendingStock () {
for(i = 0; i < data.USDTPairs.length; i++){
const USDTPair = data.USDTPairs[i] + "USDT";
binance.prevDay(USDTPair, (error, prevDay, symbol) => {
if(prevDay.priceChangePercent < -2){
downStocks.push(symbol)
}
});
}
return downStocks;
}
I have tried to also use async in for loop because the getDownTrendinStock function returns an empty array before for loop is finished. I didn't find the right way to do that because I was confused with all async, promise and callback stuff. What is the right statement to use in this situation?
Output:
All stocks that are down:
Wanted output:
All stocks that are down: [BTCUSDT, TRXUSDT, ATOMUSDT...]
I think the main issue in the code you posted is that you are mixing callbacks and promises.
This is what's happening in your getDownTrendingStock function:
You start iterating over the data.USDTPairs array, picking the first element
You call binance.prevDay. This does nothing yet, because its an asynchronous function that takes a bit of time. Notably, no data is added to downStocks yet.
You continue doing 1-2, still, no data is added
You return downStocks, which is still empty.
Your function is complete, you print the empty array
Now, at some point, the nodejs event loop continues and starts working on those asynchronous tasks you created earlier by calling binance.prevDay. Internally, it probably calls an API, which takes time; once that call is completed, it calls the function you provided, which pushes data to the downStocks array.
In summary, you didn't wait for the async code to complete. You can achieve this in multiple ways.
One is to wrap this in a promise and then await that promise:
const result= await new Promise((resolve, reject) => {
binance.prevDay(USDTPair, (error, prevDay, symbol) => {
if (error) {
reject(error);
} else {
resolve({prevDay, symbol});
}
});
});
if(result.prevDay.priceChangePercent < -2){
downStocks.push(result.symbol)
}
Note that you can probably also use promisify for this. Also, this means that you will wait for one request to finish before starting the next, which may slow down your code considerably, depending on how many calls you need; you may want to look into Promise.all as well.
Generally speaking, I use two technics:
const asyncFunc = () => {smthAsync};
const arrayToProcess = [];
// 1
const result = await arrayToProcess.reduce((acc, value) => acc.then(() => asyncFunc(value)), Promise.resolve(someInitialValue));
// 2
// here will be eslint errors
for(let i = 0 i < arrayToProcess.length; i+=1) {
const processResult = await asyncFunc(value);
// do with processResult what you want
};

Await doesnt wait in async function [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 2 years ago.
I'm learning async/await but I really don't know what is wrong here. Why is my myArray always an empty when I use await? How can I solve this?
function getFileAsBinary(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
const preparePhotos = async (photoFiles, myArray) => {
photoFiles.forEach(async (photoFile) => {
const photoBinary = await getFileAsBinary(photoFile);
myArray.push("something")
});
}
// ----------------------------------------------------
const myArray= [];
const photos = [file1,file2];
await preparePhotos(photos,myArray);
console.log(myArray); // getting [] here, why is it empty?
The callbacks passed to the forEach are not synchronous, it can work, but not as expected. When the iteration is done the async function "preparePhotos" will return immediately.
I think you should use a for loop instead.
PD: not sure if this question is a duplicated of: Using async/await with a forEach loop
When you use await inside the forEach loop, you are telling the js engine to "wait" inside the lambda, not the overall function.
If you want to be able to await the preparePhotos function, you will need to some how get "await" out of the lambda.
Start by defining an array of promises
let promises = [];
Then instead of awaiting the promise returned by getFileAsBinary, append it to the list
photoFiles.forEach((photoFile) => {
promises.push(getFileAsBinary(photoFile));
})
You now have a list of promises, and can use Promise.all to await all promises in the list
let myArray = await Promise.all(promises);
Promise#all returns a list of all values resolved by the promises within the original array, so it will return effectively your original "myArray".
Alternatively, use a for of loop (MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
for(let elem of photoFiles) {
let binary = await getFileAsBinary(elem);
myArray.push(binary);
}

How to get .map to complete before returning result in javascript? [duplicate]

This question already has answers here:
Use async await with Array.map
(9 answers)
Closed 2 years ago.
I'm struggling with using .map and await. How do I get the below to wait until the map has completed?
export async function buildDeviceArray(userIdList){ //userIdList is an array of ids
await userIdList.map(myFunction2)
return deviceArray1 // returns straight away, rather than waiting for the .map to complete
async function myFunction2(item) {
let user = await wixData.get("OurUsers", item)
let device = await wixData.get("Devices", user.deviceDefault)
let output = { "label": device.deviceReference, "value": user.deviceDefault }
if (user.deviceDefault) deviceArray1.push(output)
console.log(deviceArray1);
return
}
This is an example of how you could use .map with an async callback function.
var originalArray = [1,2,3,4];
async function multiplyBy2(x) {
await new Promise(r => setTimeout(r, 5000)); // half second
return 2*x;
}
async function run() {
var promiseArray = originalArray.map(multiplyBy2)
var answerArray = await Promise.all(promiseArray);
console.log(answerArray);
}
run();

If I await 2 functions can I guarantee that the return object will have both values

I have the following code
module.exports = async function (req, res) {
const station1 = await getStation('one')
const station2 = await getStation('two')
return { stations: [station1, station2] }
}
Can I be guaranteed that when the final return value is sent it will definitely have both station1 and station2 data in them, or do I need to wrap the function call in a Promise.all()
As you have it, it is guaranteed that the return statement will only be executed when the two getStation() promises have resolved.
However, the second call to getStation will only happen when the first promise has resolved, making them run in serial. As there is no dependency between them, you could gain performance, if you would run them in parallel.
Although this can be achieved with Promise.all, you can achieve the same by first retrieving the two promises and only then performing the await on them:
module.exports = async function (req, res) {
const promise1 = getStation('one');
const promise2 = getStation('two');
return { stations: [await promise1, await promise2] }
}
Now both calls will be performed at the "same" time, and it will be just the return statement that will be pending for both promises to resolve. This is also illustrated in MDN's "simple example".
The await keyword actually makes you "wait" on the line of code, while running an async action.
That means that you don't proceed to the next line of code until the async action is resolved. This is good if your code has a dependency with the result.
Example:
const res1 = await doSomething();
if(res1.isValid)
{
console.log('do something with res1 result');
}
The following code example will await a promise that gets resolved after three seconds. Check the date prints to the console to understand what await does:
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p = new Promise(resolve => {
setTimeout(() => resolve({}),3000);
});
await p;
console.log(new Date());
}
f1();
ES6Console
BTW, In your case, since you don't use the result of station1 it's better using Promise.all to work parallel.
Check this example (it will run for 3 seconds instead of 4 seconds the way you coded above):
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p1 = new Promise(resolve => {
setTimeout(() => resolve({a:1}),3000);
});
// Resolve after one second
var p2 = new Promise(resolve => {
setTimeout(() => resolve({a:2}),1000);
});
// Run them parallel - Max(three seconds, one second) -> three seconds.
var res = await Promise.all([p1,p2]);
console.log(new Date());
console.log('result:' + res);
}
f1();
ES6Console.
If either of await getStation('one') or await getStation('two') fails an exception will be thrown from the async function. So you should always get the resolved value from both promises.
You can rewrite your function as follows to use Promise.all
module.exports = async function (req, res) {
try{
const [station1, station2] = await Promise.all([
getStation('one'),
getStation('two')
]);
return { stations: [station1, station2] };
} catch (e) {
throw e;
}
}

Categories

Resources