I have a node module that exports a promise and resolves a database connection. When it resolves I use the connection to query records which is another async operation. Can I do both of these async actions with 1 await?
In this case the querying async call is dependent on the async promise resolving to a db connection.
Module
module.exports = {
db: new Promise((acc, rej) => {
if (!db.authenticated) {
sequelize.authenticate()
.then((res) => {
db.authenticated = true;
acc(db);
})
.catch((err) => {
rej(err)
});
} else {
acc(db);
}
})
};
usage
const db = require('../db/db.js').db;
const existingUser = await db.Person.findOne({where : {email : body.email}});
In response to my comment using await Promise.all([first(), second()]);:
The promise.All() method will return a single promise that finally resolves when all the promises pass as an iterable or when the iterable does not contain any promises. It will reject with the reason of the first promise that rejects.
Example
async function one() {
return new Promise(resolve => {
resolve('One')
})
}
async function two() {
return new Promise(resolve => {
resolve('Two')
})
}
async function run() {
return await Promise.all([one(), two()]); // One await
}
run().then((response) => {
// Access Individually
console.log(response[0]); // One
console.log(response[1]); // Two
// Access Together
console.log(response);
})
And to respond to your recent comment. To pass the value from one promise to the other, if the second function is dependent on that parameter. We might do something like this.
Example 2
async function first() {
return new Promise(resolve => {
resolve('First') // Resolve 'first'
})
}
async function second(response) {
return new Promise(resolve => {
resolve(response); // first() ran, then we appended '& second', then resolve our second response
})
}
async function run() {
// Wait for first() response, then call second() with response + append 'second'
return await first().then((response) => second(response + ' & second'))
}
run().then((response) => {
// Output: first & second
console.log(response)
})
Documentation: promise.All() - MDN
Related
This question already has answers here:
Timeout in async/await
(3 answers)
Closed 1 year ago.
In my express application, I am making call to 2 APIs. The 2nd API is managed by 3rd party and sometimes can take more than 5 seconds to respond. Hence, I want to just wait for 1 second for the API to respond. If it does not, just proceed with data from 1st API.
Below is the mock-up of the functions being called.
I am thinking to use setTimeout to throw error if the API takes more than 1 second. If the API responds within 1 second then I just cancel the setTimeout and no error is ever thrown.
But there is problem with this approach:
setTimeout errors cannot be catched using try...catch block.
I cannot use axios's timeout option, as I still need to wait for the 2nd API to finish the processing and save the data in the DB. This will ofcourse, can happen later, when the 2nd API call finishes.
// Function to simulate it's taking time.
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Track whether it took time.
let isTimeOut = false
async function test() {
console.log('starting')
try {
const one = await apiCall1()
const myt = setTimeout(() => {
console.log('Its taking time, skip the 2nd API Call')
isTimeOut = true
throw new Error('Its taking time')
})
const two = await apiCall2(myt)
} catch (error) {
console.log(error)
}
saveInDB({ ...one, ...two })
}
async function apiCall2(timeOutInstance) {
console.log('start-apiCall')
await cWait(1800)
clearTimeout(timeOutInstance)
if (isTimeOut) saveInDB()
console.log('done-apiCall')
}
async function apiCall1() {
await cWait(5)
}
async function saveInDB(data) {
console.log('saveInDB')
}
test()
please note, this is not the answer as it was when it was accepted
as I misread the question and failed to call saveInDB in a timed out
situation
Promise.race seems perfect for the job
Also, you'd actually use your cWait function, not for mock-up, but to actually do something useful ... win the race :p
const api2delay = 800;
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const TIMEDOUT = Symbol('TIMEDOUT');
async function cReject(ms) {
return new Promise((_, reject) => setTimeout(reject, ms, TIMEDOUT));
}
function apiCall2timeout(timeoutCallback) {
const resultApi2 = apiCall2();
const timeout = cReject(1000);
return Promise.race([resultApi2, timeout])
.catch(e => {
if (e === TIMEDOUT) {
resultApi2.then(timeoutCallback);
} else {
throw e;
}
});
}
async function test() {
console.log('starting')
let one, two;
try {
one = await apiCall1();
two = await apiCall2timeout(saveInDB);
} catch (error) {
console.log('error', error)
}
saveInDB({
...one,
...two
})
}
async function apiCall2() {
console.log('start-apiCall2')
await cWait(api2delay)
console.log('done-apiCall2')
return {
api2: 'done'
}
}
async function apiCall1() {
await cWait(5)
return {
api1: 'done'
}
}
async function saveInDB(data) {
console.log('saveInDB', data)
}
test()
Note: I changed where one and two were declared since const is block scoped
I you run with await cWait(800) in apiCall2, the saveInDB will run with both data.
But if you run await cWait(1800), the saveInDB will run 2 times.
// Function to simulate it's taking time.
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const promiseTimeout = function (ms, promise) {
// Create a promise that rejects in <ms> milliseconds
let timeout = new Promise((resolve, reject) => {
let id = setTimeout(() => {
clearTimeout(id);
reject('Timed out in ' + ms + 'ms.')
}, ms)
})
// Returns a race between our timeout and the passed in promise
return Promise.race([
promise,
timeout
])
}
// Track whether it took time.
let isTimeOut = false
async function test() {
console.log('starting')
const one = await apiCall1() // get data from 1st API
let two = {};
try {
two = await promiseTimeout(1000, apiCall2())
} catch (error) {
isTimeOut = true;
console.log(error)
}
saveInDB({ ...one, ...two })
}
async function apiCall2() {
console.log('start-apiCall')
await cWait(800)
console.log('done-apiCall', isTimeOut)
if (isTimeOut) {
saveInDB({ 2: 'two' })
}
return { 2: 'two' }
}
async function apiCall1() {
await cWait(5)
return { 1: 'one' }
}
async function saveInDB(data) {
console.log('saveInDB', data)
}
test()
I'm using the async.eachLimit function to control the maximum number of operations at a time.
const { eachLimit } = require("async");
function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
As you can see, I can't declare the myFunction function as async because I don't have access to the value inside the second callback of the eachLimit function.
You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.
Your code is a good example of the main risk: not propagating all errors safely. Read why there.
In addition, the use of async/await can make the same traps even more surprising. Compare:
let p = new Promise(resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.
with a naive (wrong) async equivalent:
let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!
Look in your browser's web console for the last one.
The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).
The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.
Since the return value of a promise constructor executor function is unused, that's bad news!
Your code
There's no reason you can't define myFunction as async:
async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
Though why use outdated concurrency control libraries when you have await?
I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:
const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)
let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;
if (op2 == null) {
throw new Error('Validation error');
}
const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
The function passed to Promise constructor is not async, so linters don't show errors.
All of the async functions can be called in sequential order using await.
Custom errors can be added to validate the results of async operations
The error is caught nicely eventually.
A drawback though is that you have to remember putting try/catch and attaching it to reject.
BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN
Throws within an async promise callback can easily be caught.
(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
or even more simply,
(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
I didn't realized it directly by reading the other answers, but what is important is to evaluate your async function to turn it into a Promise.
So if you define your async function using something like:
let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};
your turn it into a promise using:
let myPromise = f()
You can then manipulate is as a Promise, using for instance Promise.all([myPromise])...
Of course, you can turn it into a one liner using:
(async () => { code with await })()
static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res = axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}
remove await and async will solve this issue. because you have applied Promise object, that's enough.
I have a promise that I'm trying to await but it is not working.
Here's my method:
async function lookupAndAddManager(id, record, fieldNameToAdd) {
console.log(`Searching ${id}`);
let promise = ups.getSearchResults(ups.Environment.Prod, ups.SearchConfig.Email, ups.emailFromId(id))
console.log("Promise", promise)
let response = await promise;
}
I'm calling it like this:
lookupAndAddManager(row.BPO, row, 'BPO_MGR')
My output is:
Searching CN=Name/OU=TEST/O=EXAMPLE
Promise Promise { }
However, the method is returning right away, the await promise does not wait for the promise to be resolved.
Why not?
The method I'm calling is also async:
export const getSearchResults = async (env: Environment, searchConfig: SearchConfig, query: string): Promise<ISearchResult> => {
return new Promise((resolve, reject) => {
... resolve/reject as appropriate ...
});
};
There is some other issue. I simulated your code and the await works fine.
async function lookupAndAddManager(id, record, fieldNameToAdd) {
console.log(`Searching ${id}`);
let promise = getSearchResults();
console.log("Promise will be promised and then wait 3 seconds, now its just unresolved promise object", promise)
let response = await promise;
console.log("After 3 seconds, we got response", response);
}
const getSearchResults = async (env, searchConfig, query) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('Yeeey, resolved'), 3000);
});
};
lookupAndAddManager(10);
I can see in Chrome task manager that the tab in which following code is running eats more and more memory, and it is not released until the promise is resolved
UPDATE
Main idea here is to use a single 'low level' method which would handle "busy" responses from the server. Other methods just pass url path with request data to it and awaiting for a valuable response.
Some anti-patterns was removed.
var counter = 1
// emulates post requests sent with ... axios
async function post (path, data) {
let response = (counter++ < 1000) ? { busy: true } : { balance: 3000 }
return Promise.resolve(response)
}
async function _call (path, data, resolve) {
let response = await post()
if (response.busy) {
setTimeout(() => {
_call(path, data, resolve)
}, 10)
throw new Error('busy')
}
resolve(response.balance)
}
async function makePayment (amount) {
return new Promise((resolve, reject) => {
_call('/payment/create', {amount}, resolve)
})
}
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
makePayment(500)
.then(() => {
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))
})
The first time you call _call() in here:
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
It will not call the resolve callback and it will return a rejected promise and thus the new Promise() you have in getBalance() will just do nothing initially. Remember, since _call is marked async, when you throw, that is caught and turned into a rejected promise.
When the timer fires, it will call resolve() and that will resolve the getBalance() promise, but it will not have a value and thus you don't get your balance. By the time you do eventually call resolve(response.balance), you've already called that resolve() function so the promise it belongs to is latched and won't change its value.
As others have said, there are all sorts of things wrong with this code (lots of anti-patterns). Here's a simplified version that works when I run it in node.js or in the snippet here in the answer:
function delay(t, val) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, val), t);
});
}
var counter = 1;
function post() {
console.log(`counter = ${counter}`);
// modified counter value to 100 for demo purposes here
return (counter++ < 100) ? { busy: true } : { balance: 3000 };
}
function getBalance () {
async function _call() {
let response = post();
if (response.busy) {
// delay, then chain next call
await delay(10);
return _call();
} else {
return response.balance;
}
}
// start the whole process
return _call();
}
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))
I'm using the async.eachLimit function to control the maximum number of operations at a time.
const { eachLimit } = require("async");
function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
As you can see, I can't declare the myFunction function as async because I don't have access to the value inside the second callback of the eachLimit function.
You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.
Your code is a good example of the main risk: not propagating all errors safely. Read why there.
In addition, the use of async/await can make the same traps even more surprising. Compare:
let p = new Promise(resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.
with a naive (wrong) async equivalent:
let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!
Look in your browser's web console for the last one.
The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).
The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.
Since the return value of a promise constructor executor function is unused, that's bad news!
Your code
There's no reason you can't define myFunction as async:
async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
Though why use outdated concurrency control libraries when you have await?
I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:
const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)
let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;
if (op2 == null) {
throw new Error('Validation error');
}
const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
The function passed to Promise constructor is not async, so linters don't show errors.
All of the async functions can be called in sequential order using await.
Custom errors can be added to validate the results of async operations
The error is caught nicely eventually.
A drawback though is that you have to remember putting try/catch and attaching it to reject.
BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN
Throws within an async promise callback can easily be caught.
(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
or even more simply,
(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
I didn't realized it directly by reading the other answers, but what is important is to evaluate your async function to turn it into a Promise.
So if you define your async function using something like:
let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};
your turn it into a promise using:
let myPromise = f()
You can then manipulate is as a Promise, using for instance Promise.all([myPromise])...
Of course, you can turn it into a one liner using:
(async () => { code with await })()
static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res = axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}
remove await and async will solve this issue. because you have applied Promise object, that's enough.