Strange behaviour of asynchtonous function with rejected promise [duplicate] - javascript

How can I change the following code so that both async operations are triggered and given an opportunity to run concurrently?
const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Do I need to do something like this?
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values

TL;DR
Don't use the pattern in the question where you get the promises, and then separately wait on them; instead, use Promise.all (at least for now):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
While your solution does run the two operations in parallel, it doesn't handle rejection properly if both promises reject.
Details:
Your solution runs them in parallel, but always waits for the first to finish before waiting for the second. If you just want to start them, run them in parallel, and get both results, it's just fine. (No, it isn't, keep reading...) Note that if the first takes (say) five seconds to complete and the second fails in one second, your code will wait the full five seconds before then failing.
Sadly, there isn't currently await syntax to do a parallel wait, so you have the awkwardness you listed, or Promise.all. (There's been discussion of await.all or similar, though; maybe someday.)
The Promise.all version is:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
...which is more concise, and also doesn't wait for the first operation to complete if the second fails quickly (e.g., in my five seconds / one second example above, the above will reject in one second rather than waiting five). Also note that with your original code, if the second promise rejects before the first promise resolves, you may well get a "unhandled rejection" error in the console (you do currently with Chrome v61; update: more recent versions have more interesting behavior), although that error is arguably spurious (because you do, eventually, handle the rejection, in that this code is clearly in an async function¹ and so that function will hook rejection and make its promise reject with it) (update: again, changed). But if both promises reject, you'll get a genuine unhandled rejection error because the flow of control never reaches const value2 = await p2; and thus the p2 rejection is never handled.
Unhandled rejections are a Bad Thing™ (so much so that soon, Node.js will abort the process on truly unhandled rejections, just like unhandled exceptions — because that's what they are), so best to avoid the "get the promise then await it" pattern in your question.
Here's an example of the difference in timing in the failure case (using 500ms and 100ms rather than 5 seconds and 1 second), and possibly also the arguably-spurious unhandled rejection error (open the real browser console to see it):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
And here we reject both p1 and p2, resulting in a non-spurious unhandled rejection error on p2:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
In a comment you've asked:
Side question: will the following force waiting for both (and discarding the results) await p1 && await p2?
This has the same issues around promise rejection as your original code: It will wait until p1 resolves even if p2 rejects earlier; it may generate an arguably-spurious (update: or temporary) unhandled rejection error if p2 rejects before p1 resolves; and it generates a genuine unhandled rejection error if both p1 and p2 reject (because p2's rejection is never handled).
Here's the case where p1 resolves and p2 rejects:
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, false);
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
...and where both reject:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
¹ "...this code is clearly in an async function..." That was true in 2017 when this question and answer were written. Since then, top-level await happened/is happening.

I think this should work:
const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);
A more verbose example is below in case it helps in understanding:
const promise1 = async() => {
return 3;
}
const promise2 = async() => {
return 42;
}
const promise3 = async() => {
return 500;
// emulate an error
// throw "something went wrong...";
}
const f1 = async() => {
try {
// returns an array of values
const results = await Promise.all([promise1(), promise2(), promise3()]);
console.log(results);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
// assigns values to individual variables through 'array destructuring'
const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]);
console.log(value1);
console.log(value2);
console.log(value3);
} catch (err) {
console.log("there was an error: " + err);
}
}
f1();

Use .catch() and Promise.all()
Make sure you handle rejections correctly and you can safely use Promises.all() without facing unhandled rejections. (Edit: clarification per discussion: not the Error unhandled rejection but simply rejections that are not being handled by the code. Promise.all() will throw the first promise rejection and will ignore the rest).
In the example below an array of [[error, results], ...] is returned to allow ease of processing results and/or errors.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => [e]);
(async _=> {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
})();
You may throw from within a catch() to stop waiting for all (and discard the results of the rest), however - you may only do it once per try/catch blocks so a flag has_thorwn need to be maintained and checked to make sure no unhandled errors happens.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let has_thrown = false;
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => {
if (has_thrown) {
console.log('not throwing', e);
} else {
has_thrown = 1;
throw e;
}
});
(async _=> {
try {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
} catch(e) {
console.log(e);
}
console.log('we are done');
})();

Resolves instead of Promises
const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err:' + e
const l = l => (console.log(l), l)
;(async function parallel() {
let task1 = reject(500, 'parallelTask1').catch(e).then(l)
let task2 = wait(2500, 'parallelTask2').catch(e).then(l)
let task3 = reject(1500, 'parallelTask3').catch(e).then(l)
console.log('WAITING')
;[task1, task2, task3] = [await task1, await task2, await task3]
console.log('FINISHED', task1, task2, task3)
})()
As was pointed out in other answers, a rejected promise might raise an unhandled exception.
This one .catch(e => e) is a neat little trick that catches the error and passes it down the chain, allowing the promise to resolve, instead of rejecting.
If you find this ES6 code ugly see friendlier here.

Related

Node js to suspend execution of statement if takes longer than certain time

I want to suspend execution of a statement if it takes more than certain time. Help me please to achieve this?
In the below given sample snippet of code, if the statement const result = await curly.get('www.google.com'); takes more than 2 seconds of time to complete execution I want to suspend execution of the statement and throw an exception.
const { curly } = require('node-libcurl');
exports.curlFetch = async () => {
try {
const result = await curly.get('www.google.com');
return result;
} catch (err) {
console.error('----------ERRORR OCCURRED----------', err);
throw err;
}
}
You can use Promise.race()
I have defined an timeout function that returns an promise that rejects after an certain amount of time. If the request is faster then the 3 seconds the result will be resolved otherwise the timeout rejects and you land into the catch block
const { curly } = require('node-libcurl');
exports.curlFetch = async () => {
try {
const request = curly.get('www.google.com');
const result = await Promise.race([request, timeout(3000)])
return result;
} catch (err) {
console.error('----------ERRORR OCCURRED----------', err);
throw err;
}
}
function timeout(ms) {
return new Promise((res, rej) => setTimeout(rej("Request took too long"), ms));
}
from msdn
The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
const fetchWithTimeout = (url, options, timeout = 2000) => {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
await fetchWithTimeout('www.google.com',{})
further reading :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
As the others have suggested, Promise.race() will continue script execution after a timeout, but it will not stop the execution of your curly function. Verify this with:
const wait = (t)=>new Promise((r,j)=> {
setTimeout(()=>r(t),t*1000)
});
Promise.race([
wait(1).then(console.log),
wait(5).then(console.log)
])
Supposing curly has an abort function on the request you can do:
const TIMEOUT_SEC = 20;
const wait = (t)=>new Promise((r,j)=> {
setTimeout(()=>r(t),t*1000);
});
let req = curlyWhatever();
let handled = false;
await Promise.race([
req.then(()=>handled=true),
wait(TIMEOUT_SEC).then(()=>{
if(handled)
return;
req.abort();
// do whatever else
})
])

How to catch error in nested Promise when async/await is used [duplicate]

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.

Possible implementation of Promise.any on JS

In Promise.race the promise returns as soon that the primary promise returns. In Promise.all returns when all promises resolves, but lasts one problem. If any of all promises rejects all others will be rejected.
Instead of it, exists a proposal for a Promise.any, the returns every promise alone, independent of each other, short-circuiting on a rejection.
const logAfterWait = (seconds) => new Promise((resolve, reject) => {
return setTimeout(() => resolve(console.log(`${time} time passed`)), seconds)
})
const watingList = [
logAfterWait(convertToSeconds(10)),
logAfterWait(convertToSeconds(30)),
logAfterWait(convertToSeconds(5))
]
const logReading = async (fn) => {
console.log(`${time}: reading file`)
await fn()
}
const readFiles = (files) => Promise.all(watingList.map(logReading))
.catch((error) => new Error(error))
The problem here is the block of event loop on the maping cause block on event loop on Promise.all, returning every results on the same time, differ from the expected result, that is, 5, 10, 30 seconds.
Can I avoid this situation on waitingList.map?
You can leverage the fact that Promise.race forms a monoid by creating a Promise that never settles:
const empty = x => new Promise((res, rej) => x); // never settling promise
const ps = [
Promise.reject(1).catch(empty),
Promise.resolve(2).catch(empty),
Promise.resolve(3).catch(empty)];
Promise.race(ps)
.then(console.log); // 2
You need to attach a catch handler to each Promise in the array though. You can probably create a utility function that does this for you.
You could think of something like this:
// a solution might just be not using async/await
const any = (promises) => new Promise((resolve, reject) => {
let errors = [];
let resolved;
const onFulfill = (value) => {
// skip if already resolved
if (resolved) { return; }
resolved = true;
// resolve with the first available value
resolve(value);
};
const onError = (error) => {
// skip if already resolved
if (resolved) { return; }
// collect error
errors = errors.concat(error);
// reject promise combinator if all promises are failed
if (errors.length === promises.length) {
reject(errors);
}
};
return promises.forEach((promise) => promise.then(
onFulfill,
onError,
));
});
const sleep = (ms) => new Promise(r => setTimeout(() => r(ms), ms));
const err = (ms) => sleep(ms).then(() => Promise.reject(ms));
// it would log 2000, since it is the first to resolve
any([sleep(3000), err(100), sleep(2000)]).then(console.info)
// it would an array of 2 failures
any([err(50), err(60)]).catch(console.error)
the block of IO
Note that there isn't any block of IO in javascript, the thread is just free to tackle any other task while waiting for the promises to be resolved.
Consequently, I came to a conclusion. We create a resolver that is an Either monad(not a pure implementation of the Either monad) that returns [err, response] over a map function.
The catch blocks are necessary to avoid the Unhandled Promise Rejection Warning.
const time = () => `${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`;
const sleep = (ms, pNumber) => new Promise((resolve, reject) => {
return pNumber < 3
? setTimeout(() => resolve(console.log(`${time()} time passed`)), ms)
: reject(null)
}).catch(null)
Promise.prototype.resolver = async (promise) => {
this._result = await Promise.all([promise])[0];
return this._result == null
? ["The time flies", promise]
: [null, promise]
}
const watingList = [
Promise.resolver(sleep(0, 0).catch(console.error)),
Promise.resolver(sleep(3000, 1).catch(console.error)),
Promise.resolver(sleep(5000, 2).catch(console.error)),
Promise.resolver(sleep(5000, 3).catch(console.error))
]
const logReading = (list) => {
return list.map(p => p.then(console.log(`${time()}: reading file`))
.catch(console.log))
}
((read) => logReading(read))(watingList)
PS: time function differs from the expected because of the evaluate time.
Resources can be found here:
1 - https://frontendmasters.com/courses/hardcore-js-v2/either-monad/

Resolving multiple promise variables in parallel using async/await [duplicate]

How can I change the following code so that both async operations are triggered and given an opportunity to run concurrently?
const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Do I need to do something like this?
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values
TL;DR
Don't use the pattern in the question where you get the promises, and then separately wait on them; instead, use Promise.all (at least for now):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
While your solution does run the two operations in parallel, it doesn't handle rejection properly if both promises reject.
Details:
Your solution runs them in parallel, but always waits for the first to finish before waiting for the second. If you just want to start them, run them in parallel, and get both results, it's just fine. (No, it isn't, keep reading...) Note that if the first takes (say) five seconds to complete and the second fails in one second, your code will wait the full five seconds before then failing.
Sadly, there isn't currently await syntax to do a parallel wait, so you have the awkwardness you listed, or Promise.all. (There's been discussion of await.all or similar, though; maybe someday.)
The Promise.all version is:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
...which is more concise, and also doesn't wait for the first operation to complete if the second fails quickly (e.g., in my five seconds / one second example above, the above will reject in one second rather than waiting five). Also note that with your original code, if the second promise rejects before the first promise resolves, you may well get a "unhandled rejection" error in the console (you do currently with Chrome v61; update: more recent versions have more interesting behavior), although that error is arguably spurious (because you do, eventually, handle the rejection, in that this code is clearly in an async function¹ and so that function will hook rejection and make its promise reject with it) (update: again, changed). But if both promises reject, you'll get a genuine unhandled rejection error because the flow of control never reaches const value2 = await p2; and thus the p2 rejection is never handled.
Unhandled rejections are a Bad Thing™ (so much so that soon, Node.js will abort the process on truly unhandled rejections, just like unhandled exceptions — because that's what they are), so best to avoid the "get the promise then await it" pattern in your question.
Here's an example of the difference in timing in the failure case (using 500ms and 100ms rather than 5 seconds and 1 second), and possibly also the arguably-spurious unhandled rejection error (open the real browser console to see it):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
And here we reject both p1 and p2, resulting in a non-spurious unhandled rejection error on p2:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
In a comment you've asked:
Side question: will the following force waiting for both (and discarding the results) await p1 && await p2?
This has the same issues around promise rejection as your original code: It will wait until p1 resolves even if p2 rejects earlier; it may generate an arguably-spurious (update: or temporary) unhandled rejection error if p2 rejects before p1 resolves; and it generates a genuine unhandled rejection error if both p1 and p2 reject (because p2's rejection is never handled).
Here's the case where p1 resolves and p2 rejects:
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, false);
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
...and where both reject:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
¹ "...this code is clearly in an async function..." That was true in 2017 when this question and answer were written. Since then, top-level await happened/is happening.
I think this should work:
const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);
A more verbose example is below in case it helps in understanding:
const promise1 = async() => {
return 3;
}
const promise2 = async() => {
return 42;
}
const promise3 = async() => {
return 500;
// emulate an error
// throw "something went wrong...";
}
const f1 = async() => {
try {
// returns an array of values
const results = await Promise.all([promise1(), promise2(), promise3()]);
console.log(results);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
// assigns values to individual variables through 'array destructuring'
const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]);
console.log(value1);
console.log(value2);
console.log(value3);
} catch (err) {
console.log("there was an error: " + err);
}
}
f1();
Use .catch() and Promise.all()
Make sure you handle rejections correctly and you can safely use Promises.all() without facing unhandled rejections. (Edit: clarification per discussion: not the Error unhandled rejection but simply rejections that are not being handled by the code. Promise.all() will throw the first promise rejection and will ignore the rest).
In the example below an array of [[error, results], ...] is returned to allow ease of processing results and/or errors.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => [e]);
(async _=> {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
})();
You may throw from within a catch() to stop waiting for all (and discard the results of the rest), however - you may only do it once per try/catch blocks so a flag has_thorwn need to be maintained and checked to make sure no unhandled errors happens.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let has_thrown = false;
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => {
if (has_thrown) {
console.log('not throwing', e);
} else {
has_thrown = 1;
throw e;
}
});
(async _=> {
try {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
} catch(e) {
console.log(e);
}
console.log('we are done');
})();
Resolves instead of Promises
const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err:' + e
const l = l => (console.log(l), l)
;(async function parallel() {
let task1 = reject(500, 'parallelTask1').catch(e).then(l)
let task2 = wait(2500, 'parallelTask2').catch(e).then(l)
let task3 = reject(1500, 'parallelTask3').catch(e).then(l)
console.log('WAITING')
;[task1, task2, task3] = [await task1, await task2, await task3]
console.log('FINISHED', task1, task2, task3)
})()
As was pointed out in other answers, a rejected promise might raise an unhandled exception.
This one .catch(e => e) is a neat little trick that catches the error and passes it down the chain, allowing the promise to resolve, instead of rejecting.
If you find this ES6 code ugly see friendlier here.

Is it an anti-pattern to use async/await inside of a new Promise() constructor?

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.

Categories