I was reading this article. In the below code the async 'foo' function returns the result of the async 'waitAndMaybeReject' function.
I am unable to understand why the catch block in 'foo' function is not executed when 'waitAndMaybeReject' throws an error.
async function waitAndMaybeReject() {
await new Promise(r => setTimeout(r, 5000));
const isHeads = false;
//const isHeads = true;
if (isHeads) return 'yay';
throw Error('Boo!');
}
async function foo() {
try {
return waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
foo();
You need to await the waitAndMaybeReject() function in order to be able to catch the exception. As it is described in the article you refer to:
By returning waitAndMaybeReject(), we're deferring to its result, so our catch block never runs.
To make it simple, when you return waitAndMaybeReject() you don't wait for it to resolve/reject in the try ... catch block.
Related
I need to catch an error from a third party library.
(async () => {
try {
var response = await func_1();
console.log("should not show this");
}
catch (err) {
console.log("should show this", err);
}
async function func_1() {
func_2();
}
async function func_2() {
func_N();
}
async function func_N() {
throw new Error("oops");
}
})();
So I need to catch func_N error in try catch but becuase func_N is async and it's called by some functions without await the try catch doesn't catch the error.
So is there any way to do this?
func_N is async and it's called by some functions without await
(and without return)
Then they need to fix that. There's nothing you can do about this otherwise.
For the code below why does "A" and "B" get printed before the error 'a'
var a = async()=>{
return new Promise(async (resolve,reject)=>{
var v = await ab().catch((err)=>{
console.log("error")
return reject(err)
})
console.log("A")
console.log("B")
})
}
function ab(){
return new Promise((resolve,reject)=>{
return reject("a")
})
}
async function e(){
try{
await a()
}catch(e){
console.log(e)
}
}
e()
The output for the above code is
error
A
B
a
Shouldn't it be "error" followed by 'a'
Why are A and B getting printed?
Because there's no reason it shouldn't. The return is in a callback function, so it only returns from that callback function. It does reject the outer promise, but doing that doesn't prevent the remainder of the code in your promise executor (the function you pass new Promise) from continuing.
There are several problems with the code shown, though.
If you already have a promise, you don't need to create a new one. In a, for instance, you have the promise from ab, so there's no reason for new Promise. This is often called the explicit Promise creation antipattern, this question's answers go into detail.
Using await in a promise executor function generally doesn't make much sense and probably means the executor is doing too much. The purpose of a promise executor is to start an asynchronous process, not to wait for one.
Using .catch/.then/.finally inside an async function is generally not best practice; just use await and try/catch.
Your a function should probably be just:
const a = async () => {
try {
await ab();
// Or you might put the `console.log`s here, there are
// reasons for each, but it doesn't matter given we're
// rethrowing the error
} catch (err) {
console.log("error")
throw err;
}
console.log("A")
console.log("B")
};
That will reject the promise a returns when ab's promise rejects, using the same rejection reason, and terminate a's logic rather than allowing it to continue; if ab's promise doesn't reject, it will run the two console.log statements.
Live Example:
const a = async () => {
try {
await ab();
// Or you might put the `console.log`s here, there are
// reasons for each, but it doesn't matter given we're
// rethrowing the error
} catch (err) {
console.log("error")
throw err;
}
console.log("A")
console.log("B")
};
function ab() {
return new Promise((resolve, reject) => {
return reject("a");
});
}
async function e() {
try {
await a();
} catch (e) {
console.log(e);
}
}
e();
I'm being told that "await is only valid in async function", even though it is in a async function. Here is my code:
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise((resolve,reject) => {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}
Why is this happening when I made it an async function? Is it because I am using a for loop? If so, how can I get the expected outcome without this error?
The function you define starting on line 1 is async.
The arrow function you define on line 2 and pass to the Promise constructor is not async.
You are also using the multiple promise anti-pattern. Get rid of the Promise constructor entirely. Just return the value when you have it. That's one of the main benefits of the async keyword.
async function uploadMultipleFiles(storageFilePaths, packFilePaths, packRoot) {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i], packFilePaths[i], packRoot) // error throws on this line
}
return "files uploaded";
} catch {
console.log(err);
throw "fail";
}
}
You can only use await inside of an async function, the error refers to the callback your passing to your new Promise (since you are entering a new function scope there).
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise((resolve,reject) => { // <========= this arrow function is not async
try { // so you cant use await inside
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}
The part where you try to construct a new Promise is actually redundant since an async function will resolve to a Promise anyways (read more here). So you could write your code as follows:
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
return "files uploaded"
} catch {
console.log(err)
throw new Error("fail");
}
}
The Promise callback isn't async
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise(async (resolve,reject) => {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}
When I create an async function in node and use await, I'm making the execution waits for a promise resolution (that can be a resolve or a rejection), what I do is put an await promise inside a try/catch block and throw an error in case of a promise rejection. The problem is, when I call this async function inside a try/catch block to catch the error in case of it, I get an UnhandledPromiseRejectionWarning. But the whole point of using await isn't waiting for the promise to resolve and return it's result? It seems like my async function is returning a promise.
Example - The code an UnhandledPromiseRejectionWarning:
let test = async () => {
let promise = new Promise((resolve, reject) => {
if(true) reject("reject!");
else resolve("resolve!");
});
try{
let result = await promise;
}
catch(error) {
console.log("promise error =", error);
throw error;
}
}
let main = () => {
try {
test();
}
catch(error){
console.log("error in main() =", error);
}
}
console.log("Starting test");
main();
async functions always return promises. In fact, they always return native promises (even if you returned a bluebird or a constant). The point of async/await is to reduce the version of .then callback hell. Your program will still have to have at least one .catch in the main function to handle any errors that get to the top.
It is really nice for sequential async calls, e.g.;
async function a() { /* do some network call, return a promise */ }
async function b(aResult) { /* do some network call, return a promise */ }
async function c() {
const firstRes = (await (a() /* promise */) /* not promise */);
const secondRes = await b(firstRes/* still not a promise*/);
}
You cannot await something without being inside a function. Usually this means your main function, or init or whatever you call it, is not async. This means it cannot call await and must use .catch to handle any errors or else they will be unhandled rejections. At some point in the node versions, these will start taking out your node process.
Think about async as returning a native promise - no matter what - and await as unwrapping a promise "synchronously".
note async functions return native promises, which do not resolve or reject synchronously:
Promise.resolve(2).then(r => console.log(r)); console.log(3); // 3 printed before 2
Promise.reject(new Error('2)).catch(e => console.log(e.message)); console.log(3); // 3 before 2
async functions return sync errors as rejected promises.
async function a() { throw new Error('test error'); }
// the following are true if a is defined this way too
async function a() { return Promise.reject(new Error('test error')); }
/* won't work */ try { a() } catch(e) { /* will not run */ }
/* will work */ try { await a() } catch (e) { /* will run */ }
/* will work */ a().catch(e => /* will run */)
/* won't _always_ work */ try { return a(); } catch(e) { /* will not usually run, depends on your promise unwrapping behavior */ }
Main must be an async function to catch async errors
// wont work
let main = () =>{
try{
test();
}catch(error){
console.log("error in main() =", error);
}
}
// will work
let main = async () =>{
try{
test();
}catch(error){
console.log("error in main() =", error);
}
}
If I add the keyword async to a function, it seems that I have to catch errors "in" that function. Sometimes it doesn't make sense to catch errors and I want to defer them to the caller as I may not know the context in which the function is called (e.g. is the caller doing res.json(e) or next(e), or neither)
Is there a way around this? So I can use async (in order to await inside the function) and defer errors to the caller?
Here is a really contrived example
https://codepen.io/anon/pen/OzEXwM?editors=1012
try {
function example(obj = {}) {
try {
obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
//try{
throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
//}catch(e){
// console.log('I do not want to catch error here'), I wan't to defer it to the caller
//}
}
return obj
} catch (e) {
console.log('example outer error')
}
}
let doit = example()
doit.test() // why doesn't 'outer error' catch this?
} catch (e) {
console.log('outer error')
}
The script ran as is will give an Uncaught Exception. But if I remove the async keyword it works (yes, I know in this example, async is silly, it's just an example)
Why can't I catch the error when doit.test() is called?
Usually, I would just comply and rework, but in trying to explain to someone else this morning, I realized, I didn't really know the answer.
Why can't I catch the error when doit.test() is called?
Because it is async. By the time it has reached the throw error part the try catch block on the outside has already been executed and passed. There is nothing to throw to so to speak.
To fix this, since async and await are just syntactic sugar for Promises you just use it's catch callback. Your test() function is going to return a promise so just add a the callback onto the returned promise
doit.test().catch(()=>{
console.log('Any uncaught exceptions will be sent to here now');
});
Demo
function example(obj = {}) {
obj.test = async() => {
throw new Error('example error')
}
return obj;
}
let doit = example()
doit.test().catch(e => {
console.log('Caught an exception: ', e.message);
});
If you want to catch error of test() function. You need to do await doit.test()
https://jsfiddle.net/1tgqvwof/
I wrapped your code in a anonymous function since await must be within async function
(async function () {
try {
async function example(obj = {}) {
try {
obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
//try{
throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
//}catch(e){
// console.log('I do not want to catch error here'), I wan't to defer it to the caller
//}
}
return obj
} catch (e) {
console.log('example outer error')
}
}
let doit = example()
await doit.test() // why doesn't 'outer error' catch this?
} catch (e) {
console.log('outer error')
}
})();