why it didn't catch the rejected promise? [duplicate] - javascript

This question already has answers here:
How to .catch a Promise.reject
(4 answers)
How to use try/catch, promise catch and async function correctly?
(1 answer)
Closed 7 months ago.
I was wondering why the try-catch block couldn't catch the promise rejected from foo.
Frustrated, need help. Thanks in advance.
function addTask(task) {
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
function test() {
try {
foo()
} catch (e) {
console.error(e)
}
}
// Uncaught (in promise) task failed
test()

It's the same reason that if you had:
const result = foo();
Then you would have a promise stored in result and not the settled value of the promise.
The asynchronous code is started, then the calling function continues.
Sometime later the promise is rejected, but by then it is too late for the code in test() to catch it.
If you had awaited foo(), which would require that test() be async, then it would be caught.
Otherwise you could need to use foo().catch(...).

Promises are asynchronous but you are not awaiting. It means code continues. So you have to await or you have to use then().catch(). Something like this:
function addTask(task) {
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
function test() {
foo().then(console.log).catch(console.error);
}
// Uncaught (in promise) task failed
test()

To simplify your example:
function foo() {
return new Promise((resolve, reject) => setTimeout(reject, 1000))
}
function test() {
try {
foo()
} catch (e) {
console.error("Error");
return;
}
console.log("OK!");
}
test()
will print "OK!" indeed have Node.js raise an UnhandledPromiseRejection, as that foo() invocation is just "throwing away" the promise result value. The catch() would only catch synchronous exceptions, not promise rejections.
Turning the function into async function test() and awaiting on foo() will correctly output Error and no unhandled rejection.

Add await before foo() call and async before test function declaration
like below
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
async function test() {
try {
await foo()
} catch (e) {
console.error(e)
}
}
// Uncaught (in promise) task failed
test()`

Related

Async await issue on nodejs

I'm learning to use async/await and this is quite confused to me. As I know, async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
async function foo() {
setTimeout(() => {
throw new Error('error');
}, 5000);
}
async function test() {
try {
await foo();
} catch (err) {
console.log(err);
}
console.log('not reached');
}
test();
Expected output: log the error after 5 secs, then log "not reach".
What actually happened: "not reach" gets logged right after I start the script, then the error gets logged after 5 seconds.
Attempts: the following code does exactly what I want, but I don't know what's the difference between those code.
Environment: nodejs 16.14.2
function foo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('foo'));
}, 5000);
});
}
async function test() {
try {
await foo();
} catch (err) {
console.log(err);
}
console.log('not reached');
}
test();
Thanks a lot!
foo needs to actually resolve
Your example doesn't work because foo doesn't actually care about the timeout. It just starts the timeout then returns immediately. What you need to do is make foo resolve when the timeout fires, which actually requires you to construct a Promise explicitly.
async function foo() {
return new Promise(resolve => {
setTimeout(resolve, 5000)
})
}
Since you return a Promise, you could technically not declare foo as async, but it doesn't matter, and (I think) it's still helpful for other developers to see that the function returns a Promise without having to look through the implementation.
In your second example, foo returns a promise, setTimeout needs to produce a promise for it to work in your example.
For example
async function foo() {
// promise returned
return new Promise(resolve => {
setTimeout(() => { resolve() } , 5000)
})
}

promise then is not the function error for Node Promise

I am using async/await to return the Promise , to use it as promoise in node script. When I am trying to make use of the return value as Promise , its giving a error a.then is not a function
here is the sample code
function test () {
//do something .......
//....
return global.Promise;
}
(async ()=> {
let a = await test();
a.then(()=> { console.log('good ')}, (err)=> { console.log()});
})();
The Promise constructor function isn't a promise, it is a tool to make promises with.
Even if it was a promise, since you are awaiting the return value of test, it would have been resolved into a value before you try to call then on it. (The point of await is that it replaces the use of then() callbacks).
You can await a function that returns a promise like this:
function test() {
return new Promise((resolve, reject) => {
if (true) {
reject("Custom error message");
}
setTimeout(() => {
resolve(56)
}, 200);
})
}
async function main() {
try {
const a = await test();
console.log(a)
} catch (e) { // this handles the "reject"
console.log(e);
}
}
main();
If you change the true to false you can test the "resolve" case.
await retrieves the resolved value from a Promise
let a = await test(); // `a` is no longer a Promise
I've put together two ways of retrieving values from a Promise
using await
(async () => {
try {
let a = await test();
console.log('Good', a);
} catch(err) {
console.log(err);
}
})();
using .then()
test().then(a => {
console.log('Good', a);
}).catch(err => {
console.log(err);
});
Please note that, the async arrow function is removed because there is no await needed.

How to handle an unexpected error from a function after result is return from function in JavaScript?

While working on asynchronous functions in JavaScript. I discovered this problem.
A function needs to perform some asynchronous task, however that task doesn't make difference to function result. So function return its result, and after a while an exception is thrown from asynchronous function, however since control is already returned so exception goes unhanded.
Note: Not to change notify I'm intentionally throwing error like that, need to only handle this exception
Code is like:
function notify() { //To send notification
setTimeout( function() { //Just to simulate an asynchronous function
throw "Exception occurred"; //An exception from async function
},2000);
return "Notified";
}
try {
let result = notify();
console.log(result); //Notified
}
catch (error) { //Never comes to catch block.
console.log(error);
}
How can to catch this exception.
Tried using Promise, however it doesn't resolved with promise because even with promise, flow or function is removed from memory as calling function have received response.
Code using Promises
function notify() { //To send notification
return new Promise ( (resolve, reject) => {
setTimeout( function() { //Just to simulate an asynchronous function
throw "Exception occurred"; //An exception from async function
},2000);
resolve("Notified");
});
}
notify()
.then( result => {
console.log(result); //Notified
})
.catch( error => { //Never comes to catch block
console.log(error);
});
How can I catch exception in JavaScript programming?
I would wrap setTimeout in a promise as:
const delay = ms => new Promise(res => setTimeout(res, ms));
That allows you to write notify as:
async function notify() {
await delay(2000);
throw new Error();
}
notify().then(/*...*/).catch(/*...*/);
The trick here is that throw is inside of an asynchronous function and not as in your case inside of a callback.
var notify = function () {
anOddTask();
return "Notified";
}
async function anOddTask() {
try {
await setTimeout( function() {
},2000);
throw new Error("Exception occurred");
}
catch (err) {
console.log(err);
}
}
try {
let result = notify();
console.log(result); //Notified
}
catch (error) { //Never comes to catch block.
console.log(error);
}
This will immediately return "Notify" and handle the rejection at some later time. If you wish that "Notify" be returned as a resolved promise than just make notify() an async function.
var notify = async function () {
anOddTask();
return "Notified";
}

How does an async mocha test resolve without returning a promise or invoking the done callback

I would love to better understand the internals of why the following example works as expected:
describe('async await', () => {
it('resolves without return', async () => {
await asyncOperation();
});
});
function asyncOperation() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 123);
});
}
Typically an async mocha test must return a promise (or execute the done callback) but in this example nothing is returned but the mocha test still works. How exactly does this work?
From the async documentation:
The async function declaration defines an asynchronous function, which returns an AsyncFunction object.
Description
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
This means that in your case, a Promise is returned, that's why your test works.
When you use the async keyword, you are implicitly returning a Promise of whatever type you actually use in the return statement of the function (in this case you aren't returning anything, so this is simply a Promise of nothing, or Promise<void> if you're into TypeScript).
Internally, a function that uses async/await gets unrolled into a number of asynchronous continuations, split at each usage of the await keyword. When the promise you are await-ing completes, the remainder of the function is resumed. It may be instructive to see how transpilers like Babel unroll your code.
This code:
function asyncOperation() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 123);
});
}
async () => {
await asyncOperation();
}
is transpiled to plain ES5 as:
"use strict";
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function asyncOperation() {
return new Promise(function (resolve) {
setTimeout(function () {
resolve();
}, 123);
});
}
_asyncToGenerator(regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return asyncOperation();
case 2:
case "end":
return _context.stop();
}
}
}, _callee, undefined);
}));
That ugly _asyncToGenerator invocation used to be your beautiful async function. It has been unrolled into explicit continuations (you can try adding more await points and logic to the function and seeing how the transpiled code changes).

How to handle errors from setTimeout in JavaScript?

Simple question about try/catch for function in setTimeout
try {
setTimeout(function () {
throw new Error('error!');
}, 300)
} catch (e) {
console.log('eeee!')
console.log(e)
}
Why doesn't catch block work?
What can I read about this?
P.S: the question is about possibility of handling errors like this. Don't answer about promises.
Functions scheduled to run with setTimeout are executed in the main loop, outside the body of code that originated them.
To handle errors, put the try-catch inside the setTimeout handler:
setTimeout(function () {
try {
throw new Error('error!');
} catch (e) {
console.error(e);
}
}, 300)
If you need to access the Error object from block that called setTimeout, use Promises:
const promise = new Promise((resolve, reject) => {
setTimeout(function () {
try {
throw new Error('error!');
resolve(); // if the previous line didn't always throw
} catch (e) {
reject(e)
}
}, 300)
})
promise
.then(result => console.log("Ok " + result))
.catch(error => console.error("Ouch " + error))
This example above is not the most elegant way of handling the case with a Promise. Instead, implement a delay(ms) function like this:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
Then call
delay(300).then(myFunction).catch(handleError)
You can find good explanation in this Node.js official doc.
The problem is that when the callback of your setTimeout() function executes the try { } catch(err) { } block is already exited. Also notice that the callback can crash Node.js process.
However if you want to handle the errors in the callback of setTimeout() function, then you can listen them by using process global EventEmitter object
process.on('uncaughtException', function(err){
console.log(err)
})
Because the catch block lexically surrounds the setTimeout call but that is not the function that throws.
The direct translation, is
setTimeout(function () {
try {
throw new Error('error!');
} catch (e) {
console.log('eeee!');
console.log(e);
}
}, 300);
A bit strange solution, but sometimes it would be useful maybe...
function globalErrorHandler(e) {
console.warn('eeee!')
console.warn(e);
}
const _setTimeoutOriginal = setTimeout;
setTimeout = function(callback, timeout) {
const args = Array.from(arguments).slice(2);
_setTimeoutOriginal(function() {
try {
callback.apply(this, args);
} catch (e) {
globalErrorHandler(e);
}
}, timeout);
};
setTimeout(function() {
throw new Error('error!');
}, 300)

Categories