I'm trying out async await right now. I thought this code should work:
async function f1() {
return new Promise(async (resolve, reject) => {
await f2();
resolve();
});
}
async function f2() {
return new Promise((resolve, reject) => {
throw new Error('error should bubble up');
});
}
async function main() {
try {
await f1();
}
catch(err) {
console.log(err.message)
}
}
main();
But instead, I would need to add a try/catch in f1's promise in order to make the error bubble up. I thought promises already catches and throw it upwards even on await statements? Otherwise, people need to put a lot of try/catch statements everywhere.
Sorry, nothing wrong here.
I just tested this on babel, it seems like they throw an error in:
return new Promise(async (resolve, reject) => {
await f2();
resolve();
});
Unhandled promise rejection Error: error should bubble up(…)
https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=async%20function%20f1()%20%7B%0A%20%20%20%20return%20new%20Promise(async%20(resolve%2C%20reject)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20await%20f2()%3B%0A%20%20%20%20%20%20%20%20resolve()%3B%0A%20%20%20%20%7D)%3B%0A%7D%0A%0Aasync%20function%20f2()%20%7B%0A%20%20%20%20return%20new%20Promise((resolve%2C%20reject)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20throw%20new%20Error('error%20should%20bubble%20up')%3B%0A%20%20%20%20%7D)%3B%0A%7D%0A%0Aasync%20function%20main()%20%7B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20await%20f1()%3B%0A%20%20%20%20%7D%0A%20%20%20%20catch(err)%20%7B%0A%20%20%20%20%20%20%20%20console.log(err.message)%0A%20%20%20%20%7D%0A%7D%0A%0Amain()%3B
It is quite hard to find the error when it is hidden or catched in this case and not rethrowned in TS. Isn't babel doing the right thing here? TS just gives me no hints of what went wrong. I know TS guidelines is to not to inject any code that could affect the runtime. But this case feels like an exception that could be made?
I'm pretty sure this is per spec, but @rbuckton and @bterlson would know better than I do.
This is working as specified. The issue here is this:
async function f1() {
return new Promise(async (resolve, reject) => {
await f2();
resolve();
});
}
You are using an async arrow function as the executor callback to the Promise constructor. Even though the arrow function is async, the Promise constructor does not observe the return value of the callback (as per the ES6 specification). When f2() eventually throws, the exception rejects the Promise created by the async arrow function. Since the Promise constructor does not observe the Promise returned by the async arrow function, the rejection will be unobserved and f1 will be forever pending.
There are two ways to properly write f1. If you truly intended to return a Promise in f1, you could instead write it as follows:
async function f1() {
return new Promise(async (resolve, reject) => {
try {
await f2();
resolve();
}
catch (e) {
reject(e);
}
});
}
However, if your intend was to simply await the result of f2, you could have written the function like this:
async function f1() {
await f2();
return;
}
Any async function automatically wraps its body in a new Promise, so there is no need to create one yourself. The only time you need to create a new Promise, is if you are consuming an asynchronous API that does not itself expose a Promise. For example, if you wanted to read a file asynchronously in NodeJS:
import { readFile } from "fs";
// marked 'async' as we are consuming 'f2' here
async function f1() {
let contents = await f2();
console.log(contents);
}
// not marked 'async' as we are manually creating a new Promise here
function f2() {
return new Promise<string>((resolve, reject) => {
readFile('somefile.txt', 'utf8', (error, data) => {
if (error) {
reject(error);
}
else {
resolve(data);
}
});
});
}
Most helpful comment
This is working as specified. The issue here is this:
You are using an async arrow function as the
executorcallback to the Promise constructor. Even though the arrow function is async, the Promise constructor does not observe the return value of the callback (as per the ES6 specification). Whenf2()eventually throws, the exception rejects the Promise created by the async arrow function. Since the Promise constructor does not observe the Promise returned by the async arrow function, the rejection will be unobserved andf1will be forever pending.There are two ways to properly write
f1. If you truly intended to return a Promise inf1, you could instead write it as follows:However, if your intend was to simply await the result of
f2, you could have written the function like this:Any
asyncfunction automatically wraps its body in a new Promise, so there is no need to create one yourself. The only time you need to create a new Promise, is if you are consuming an asynchronous API that does not itself expose a Promise. For example, if you wanted to read a file asynchronously in NodeJS: