Await on an asynchronous function not resolving before continuing - javascript

I have a function that looks like this:
async function sync(req, res, done){
await createRecords().then(async ()=> {
await Promise.all(
[
quantityReport(),
listings(),
productIdentifiers(),
productChildren()
])
}).then(async ()=>{
await saveAll()
} ).then(await createCSV);
}
module.exports = sync
I am calling it like this inside a switch:
// const saveRecords = require('../scripts/saveRecords.js') <- for reference
await saveRecords;
My problem is that the program continues before saveRecords finishes and I cannot figure out why.
All of the functions in Promise.all are asynchronous functions.
If I call sync() directly in saveRecords.js it works fine.
Thanks.
edit
createCSV also works fine in other locations in the program. It's imported to this file like this:
const {createCSV, uploadReports} = require('../scripts/createCSV.js')
//in createCSV.js
module.exports = createCSV;

I'd refactor your function as such (by the way, sync doesn't sounds like a great name for your function, write something more obvious).
async function sync(req, res, done){
try{
await createRecords()
const _res = await Promise.all([
quantityReport(),
listings(),
productIdentifiers(),
productChildren()
])
if(_res) {
await saveAll()
await createCSV()
}
return
}
catch(err){
throw new Error(err)
}
}
module.exports = sync

As I mentioned in the comments using async/await with then from the Promise API (see also fetch) is an odd thing to do. Use one or the other. But the key issue is that you're not calling the sync function await sync().
Here's an quick example of simply using async/await.
function mockCall(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 1000);
});
}
async function sync() {
const first = await mockCall(1);
const twoToFive = await Promise.all([
mockCall(2),
mockCall(3),
mockCall(4),
mockCall(5)
]);
const six = await mockCall(6);
const seven = await mockCall(7);
console.log([ first, twoToFive, six, seven ]);
}
(async function main() {
await sync();
console.log('Let the processing resume!');
})();

Related

Which is better Promise.all or nested async await?

I have two blocks of code. First is using async await
async sendEmailNotifications() {
try {
const users = await User.find(...)
const promises = users.map(async(user) => {
const _promises = user.appId.map(async(app) => {
const todayVisitorsCount = await Session.count({...})
const yesterdayVisitorsCount = await UserSession.count({...})
const emailObj = {
todayVisitorsCount,
yesterdayVisitorsCount
}
const sendNotification = await emailService.analyticsNotification(emailObj)
})
await Promise.all(_promises)
})
return promises
} catch (err) {
return err
}
}
(await sendEmailNotifications())
And then I have using Promise.all
sendEmailNotifications() {
const users = await User.find(...)
const promises = users.map((user) => {
const allPromises = []
user.appId.map((app) => {
allPromises.push(UserSession.count({...}))
allPromises.push(Session.count({...}))
})
const data = await Promise.all(allPromises)
const emailObj = {
todayVisitorsCount: data[0],
yesterdayVisitorsCount: data[1]
}
const sendNotification = await emailService.analyticsNotification(emailObj)
})
return promises
}
sendNotification.then((data) => console.log(data))
Now I need to know which piece of code will faster execute? One is with series(async await) and one is with parellel(Promise.all). Which has better performance?
In the first code, you have two separate await statements:
const todayVisitorsCount = await Session.count({...})
const yesterdayVisitorsCount = await UserSession.count({...})
whereas in the second, you only have one, before a Promise.all:
const data = await Promise.all(allPromises)
In the first code, the second Promise will only initialize after the first Promise has finished, resulting in a longer time required before the script ends. For example:
const fn = () => new Promise(resolve => setTimeout(resolve, 1000));
console.log('start');
(async () => {
await fn();
await fn();
console.log('two awaits done');
})();
(async () => {
await Promise.all([fn(), fn()]);
console.log('Promise.all done');
})();
The version without Promise.all pauses the function when the first call of fn() is made, and waits for the Promise returned by fn() to resolve (1000 ms) before proceeding to the next line. The next line calls fn() again, and the await waits for it to complete (1000 more ms).
In contrast, the Promise.all version calls both fn()s immediately - both Promises are initialized, and the await that pauses the function is waiting for both Promises to complete. There's no down time between the initialization of the first Promise and the initialization of the second Promise.
So, the Promise.all version will run more significantly more quickly than the version with two awaits. Using Promise.all will be preferable unless the first Promise (UserSession.count) must be completed before the second Promise (Session.count) starts.
With destructuring and without unnecessary variables, this is how I would clean up your Promise.all code, you might consider it to be a bit more readable:
async sendEmailNotifications() {
const users = await User.find();
return users.map(async (user) => {
const [todayVisitorsCount, yesterdayVisitorsCount] = await Promise.all([
UserSession.count(),
Session.count()
]);
await emailService.analyticsNotification({ todayVisitorsCount, yesterdayVisitorsCount });
});
}

Catch Promise rejection at a later time [duplicate]

This question already has answers here:
Waiting for more than one concurrent await operation
(4 answers)
Closed 4 years ago.
How do I retrieve the result of a promise at a later time? In a test, I am retrieving an email before sending further requests:
const email = await get_email();
assert.equal(email.subject, 'foobar');
await send_request1();
await send_request2();
How can I send the requests while the slow email retrieval is going on?
At first, I considered awaiting the email later:
// This code is wrong - do not copy!
const email_promise = get_email();
await send_request1();
await send_request2();
const email = await email_promise;
assert.equal(email.subject, 'foobar');
This works if get_email() is successful, but fails if get_email() fails before the corresponding await, with a completely justified UnhandledPromiseRejectionWarning.
Of course, I could use Promise.all, like this:
await Promise.all([
async () => {
const email = await get_email();
assert.equal(email.subject, 'foobar');
},
async () => {
await send_request1();
await send_request2();
},
]);
However, it makes the code much harder to read (it looks more like callback-based programming), especially if later requests actually depend on the email, or there is some nesting going on. Is it possible to store the result/exception of a promise and await it at a later time?
If need be, here is a testcase with mocks that sometimes fail and sometimes work, with random timings. It must never output UnhandledPromiseRejectionWarning.
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const send_request1 = () => wait(300), send_request2 = () => wait(200);
async function get_email() {
await wait(Math.random() * 1000);
if (Math.random() > 0.5) throw new Error('failure');
return {subject: 'foobar'};
}
const assert = require('assert');
async function main() {
// catch possible error
const email_promise = get_email().catch(e => e);
await send_request1();
await send_request2();
// wait for result
const email = await email_promise;
// rethrow eventual error or do whatever you want with it
if(email instanceof Error) {
throw email;
}
assert.equal(email.subject, 'foobar');
};
(async () => {
try {
await main();
} catch(e) {
console.log('main error: ' + e.stack);
}
})();
In case it's guaranteed that promise rejection will be handled later, a promise can be chained with dummy catch to suppress the detection of unhandled rejection:
try {
const email_promise = get_email();
email_promise.catch(() => {}); // a hack
await send_request1();
await send_request2();
const email = await email_promise;
assert.equal(email.subject, 'foobar');
} catch (err) {...}
The problem with this approach is that there are two concurrent routines but the code doesn't express this, this is a workaround for what is usually done with Promise.all. The only reason why this workaround is feasible is that there are only 2 routines, and one of them (get_email) requires to be chained with then/await only once, so a part of it (assert) can be postponed. The problem would be more obvious if there were 3 or more routines, or routines involved multiple then/await.
In case Promise.all introduces unwanted level of lambda nesting, this can be avoided by writing routines as named functions, even if they aren't reused anywhere else:
async function assertEmail() {
const email = await get_email();
assert.equal(email.subject, 'foobar');
}
async function sendRequests() {
await send_request1();
await send_request2();
}
...
try {
await Promise.all([assertEmail(), sendRequests()]);
} catch (err) {...}
This results in clean control flow and verbose but more intelligible and testable code.
So, I want to explain why we behave this way in Node.js:
// Your "incorrect code" from before
const email_promise = get_email(); // we acquire the promise here
await send_request1(); // if this throws - we're left with a mess
await send_request2(); // if this throws - we're left with a mess
const email = await email_promise;
assert.equal(email.subject, 'foobar');
That is, the reason we behave this way is to not deal with the "multiple rejections and possibly no cleanup" scenario. I'm not sure how you ended up with the long code for Promise.all but this:
await Promise.all([
async () => {
const email = await get_email();
assert.equal(email.subject, 'foobar');
},
async () => {
await send_request1();
await send_request2();
},
]);
Can actually be this:
let [email, requestData] = await Promise.all([
get_email(),
send_request1().then(send_request2)
]);
// do whatever with email here
It's probably what I would do.

Javascript: SyntaxError: await is only valid in async function

I am on Node 8 with Sequelize.js
Gtting the following error when trying to use await.
SyntaxError: await is only valid in async function
Code:
async function addEvent(req, callback) {
var db = req.app.get('db');
var event = req.body.event
db.App.findOne({
where: {
owner_id: req.user_id,
}
}).then((app) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 6000)
})
// I get an error at this point
let result = await promise;
// let result = await promise;
// ^^^^^
// SyntaxError: await is only valid in async function
}
})
}
Getting the following error:
let result = await promise;
^^^^^
SyntaxError: await is only valid in async function
What am I doing wrong?
You can run await statement only under async function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
So, you can write your
}).then((app) => {
as
}).then(async (app) => {
addEvent is a mixture of async..await and raw promises. await is syntactic sugar for then. It's either one or another. A mixture results in incorrect control flow; db.App.findOne(...).then(...) promise is not chained or returned and thus is not available from outside addEvent.
It should be:
async function addEvent(req, callback) {
var db = req.app.get('db');
var event = req.body.event
const app = await db.App.findOne({
where: {
owner_id: req.user_id,
}
});
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 6000)
})
let result = await promise;
}
Generally plain callbacks shouldn't be mixed with promises. callback parameter indicates that API that uses addEvent may need to be promisified as well.
async/await only works if the immediate function has the async keyword, you need this:
...
}).then(async app => { // <<<< here
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 6000)
})
// I get an error at this point
let result = await promise;
// let result = await promise;
// ^^^^^
// SyntaxError: await is only valid in async function
}
})
You can use await only inside a function which is async. Also you can await only a piece of code that returns a promise.
Here you are using await inside a different context. Better you use then() here to solve the problem.
await only works if the immediate function that encloses it is async.

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

When using async await, how do you specify a callback?

I was looking at how to use transactions in:
https://node-postgres.com/features/transactions
But in the following code example:
const { Pool } = require('pg')
const pool = new Pool()
(async () => {
// note: we don't try/catch this because if connecting throws an exception
// we don't need to dispose of the client (it will be undefined)
const client = await pool.connect()
try {
await client.query('BEGIN')
const { rows } = await client.query('INSERT INTO users(name) VALUES($1) RETURNING id', ['brianc'])
const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)'
const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo']
await client.query(insertPhotoText, insertPhotoValues)
await client.query('COMMIT')
} catch (e) {
await client.query('ROLLBACK')
throw e
} finally {
client.release()
}
})().catch(e => console.error(e.stack))
It seems that the function will execute immediately. Also there doesn't seem to be a way to specify a callback. Would it make sense to place the entire block from "(async()...." into a function, and then in the final statement before the end of the try block, add :
await callbackfunction();
Does that make sense? What would be a better way to add a callback function ?
The point of await is that you don't use a callback. It returns the result of resolving the promise.
Without await:
do_something_asyc.then(function (data) { alert(data); });
With await:
var data = await do_something_asyc();
alert(data);

Categories

Resources