Why add `async` to a promise callback [duplicate] - javascript

I've been trying to get a conceptual understanding of why the following code doesn't catch the throw. If you remove the async keyword from the new Promise(async (resolve, ... part then it works fine, so it has to do with the fact that the Promise executor is an async function.
(async function() {
try {
await fn();
} catch(e) {
console.log("CAUGHT fn error -->",e)
}
})();
function fn() {
return new Promise(async (resolve, reject) => {
// ...
throw new Error("<<fn error>>");
// ...
});
}
The answers here, here, and here repeat that "if you're in any other asynchronous callback, you must use reject", but by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here (and if they do, I don't understand how).
If instead of throw we use reject, the above code works fine. I'd like to understand, fundamentally, why throw doesn't work here. Thanks!

This is the async/await version of the Promise constructor antipattern!
Never ever use an async function as a Promise executor function (even when you can make it work1)!
[1: by calling resolve and reject instead of using return and throw statements]
by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here
They could as well. A simple example where it cannot work is
new Promise(async function() {
await delay(…);
throw new Error(…);
})
which is equivalent to
new Promise(function() {
return delay(…).then(function() {
throw new Error(…);
});
})
where it's clear now that the throw is inside an asynchronous callback.
The Promise constructor can only catch synchronous exceptions, and an async function never throws - it always returns a promise (which might get rejected though). And that return value is ignored, as the promise is waiting for resolve to be called.

because the only way to "communicate" to the outside world from within a Promise executor is to use the resolve and reject functions. You could use the following for your example:
function fn() {
return new Promise(async (resolve, reject) => {
// there is no real reason to use an async executor here since there is nothing async happening
try {
throw new Error('<<fn error>>')
} catch(error) {
return reject(error);
}
});
}
An example would be when you want to do something that has convenient async functions, but also requires a callback. The following contrived example copies a file by reading it using the async fs.promises.readFile function with the callback based fs.writeFile function. In the real world, you would never mix fs functions like this because there is no need to. But some libraries like stylus and pug use callbacks, and I use something like this all the time in those scenarios.
const fs = require('fs');
function copyFile(infilePath, outfilePath) {
return new Promise(async (resolve, reject) => {
try {
// the fs.promises library provides convenient async functions
const data = await fs.promises.readFile(infilePath);
// the fs library also provides methods that use callbacks
// the following line doesn't need a return statement, because there is nothing to return the value to
// but IMO it is useful to signal intent that the function has completed (especially in more complex functions)
return fs.writeFile(outfilePath, data, (error) => {
// note that if there is an error we call the reject function
// so whether an error is thrown in the promise executor, or the callback the reject function will be called
// so from the outside, copyFile appears to be a perfectly normal async function
return (error) ? reject(error) : resolve();
});
} catch(error) {
// this will only catch errors from the main body of the promise executor (ie. the fs.promises.readFile statement
// it will not catch any errors from the callback to the fs.writeFile statement
return reject(error);
// the return statement is not necessary, but IMO communicates the intent that the function is completed
}
}
}
Apparently everyone says this is an anti-pattern, but I use it all the time when I want to do some async stuff before doing something that can only be done with a callback (not for copying files like my contrived example). I don't understand why people think it is an anti-pattern (to use an async promise executor), and haven't seen an example yet that has convinced me that it should be accepted as a general rule.

Related

Node JS: Not calling promise reject() callback stops the execution of the program

It seems I misunderstand the Promise mechanism. In the following code:
async function CreateSession()
{
var sessionId = await new Promise((resolve, reject) => {
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
log.debug("Session created successfully")
resolve(body["session-id"]);
}
else
{
log.error("Failed to create session:");
log.error(error);
// reject(error);
}
});
});
log.debug(`Session Id: ${sessionId}`);
return sessionId;
}
The reject() is commented on purpose to understand the mechanism.
When calling the above CreateSession() function as follows and making sure an error is happenning:
async function MyFunction()
{
var sessionId = await CreateSession();
AnotherCode();
}
The entire program stops, the code after await in CreateSession is never reached, AnotherCode() is never called, and even if I put a handler for uncaught exception it doesn't catch any error.
My questions:
Why does the program stops when reject() is not called?
If it's an error why it is not called by the handler for uncaught exceptions?
How do I ignore the an error when a promise fails to run its code?
Why does the program stops when reject() is not called?
Because of the await. The program waits for the promise to either resolve or reject. It can't detect somehow that the code inside the promise constructor has finished executing (even if it could - what should be the "standard" behavior?) and there is no more code to come.
From the MDN:
An async function can contain an await expression that pauses the execution of the async function to wait for the passed Promise's resolution, then resumes the async function's execution and evaluates as the resolved value.
For some "under the hood" insights, you might want to look at the ES5 code generated by transpilers like Babel (IIRC it uses generators to handle this).
If it's an error why it is not called by the handler for uncaught exceptions?
The program doesn't know that error is an error if it's neither thrown nor passed using reject.
How do I ignore the an error when a promise fails to run its code?
Either resolve (instead of reject) or reject and do the classical try/catch around the await code
For a start you're mixing 2 concepts that achieve the same thing, the creation of a Promise. You are mixing async/await and new Promise.
That makes it actually like you have 2 chained Promises.
Why does the program stops when reject() is not called?
Because you are returning a Promise and that by itself demands that either a resolve or a reject callbacks are called.
If it's an error why it is not called by the handler for uncaught exceptions?
There is no actual error, the only thing that happens is that your if clause is not met.
How do I ignore the an error when a promise fails to run its code?
Again, the Promise does not fail to run it's code, you're failing to understand the concept.
To make it easier either use something like :
function CreateSession(){
return new Promise((resolve, reject) => {
if( <yourconditionhere> ){
resolve(res);
}else{
reject(error);
}
});
}
CreateSession
.then((res) => {
// code to execute in case of success (resolve)
// receiving as argument the argument of the resolve call.
AnotherCode();
})
.catch( error => {
// code to execute in case of non-success (reject)
// receiving as argument the argument of the reject call.
});
or
async function CreateSession(){
let res = await request(options, function (error, response, body) {
if( <yourconditionhere> ){
return ...;
}else{
return ...;
}
}
return res;
}
async function MyFunction()
{
var sessionId = await CreateSession();
AnotherCode();
}

Early returning inside an asynchronous function

Assume the scenario where you have to call an asynchronous function, but you are not really interested about success/failure situation of that function. In that case what are the pros and cons in following two patterns stated below with respect to call stack, callback queue and event loop
Pattern-1
async setSomething() {
try {
set(); // another async function
} catch (err) {
// log the error here
}
return true;
}
Pattern-2
async setSomething() {
try {
await set(); // another async function
} catch (err) {
// log the error here
}
return true;
}
Pattern 1 does not catch any errors that may occur during asynchronous operations in the set function - any errors will result in an unhandled Promise rejection, which should be avoided. Pattern 1 will only catch errors that occur during set's synchronous operations (such as, when setting up a fetch request), which are not likely to occur in most situations.
Example:
// open your browser's console to see the uncaught rejection
const set = () => new Promise((_, reject) => setTimeout(reject, 500));
async function setSomething() {
try {
set(); // another async function
} catch (err) {
console.log('err');
}
return true;
}
setSomething();
So, pattern 2 is likely preferable. If you don't care about the result of the asynchronous call, then don't await or call .then when you call setSomething().
Or, for something this simple, you might consider using Promise methods only, no async function needed:
const setSomething = () => set()
.catch((err) => {
// log the error here
});
This answer is a rather unconventional advice to the question than an actual answer to the examples posted by OP.
not really interested about success/failure situation of that function
If the above statement is the case, then it means, the return is not dependent on the result of the async invocation.
When you're not bothered about the return of the async invocation, it's better off to not use async/await or any type of promise at all. You could just invoke the function like any other function and process with the rest of the code.

Why JS Promise's async executor and async handler methods behave differently? [duplicate]

I've been trying to get a conceptual understanding of why the following code doesn't catch the throw. If you remove the async keyword from the new Promise(async (resolve, ... part then it works fine, so it has to do with the fact that the Promise executor is an async function.
(async function() {
try {
await fn();
} catch(e) {
console.log("CAUGHT fn error -->",e)
}
})();
function fn() {
return new Promise(async (resolve, reject) => {
// ...
throw new Error("<<fn error>>");
// ...
});
}
The answers here, here, and here repeat that "if you're in any other asynchronous callback, you must use reject", but by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here (and if they do, I don't understand how).
If instead of throw we use reject, the above code works fine. I'd like to understand, fundamentally, why throw doesn't work here. Thanks!
This is the async/await version of the Promise constructor antipattern!
Never ever use an async function as a Promise executor function (even when you can make it work1)!
[1: by calling resolve and reject instead of using return and throw statements]
by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here
They could as well. A simple example where it cannot work is
new Promise(async function() {
await delay(…);
throw new Error(…);
})
which is equivalent to
new Promise(function() {
return delay(…).then(function() {
throw new Error(…);
});
})
where it's clear now that the throw is inside an asynchronous callback.
The Promise constructor can only catch synchronous exceptions, and an async function never throws - it always returns a promise (which might get rejected though). And that return value is ignored, as the promise is waiting for resolve to be called.
because the only way to "communicate" to the outside world from within a Promise executor is to use the resolve and reject functions. You could use the following for your example:
function fn() {
return new Promise(async (resolve, reject) => {
// there is no real reason to use an async executor here since there is nothing async happening
try {
throw new Error('<<fn error>>')
} catch(error) {
return reject(error);
}
});
}
An example would be when you want to do something that has convenient async functions, but also requires a callback. The following contrived example copies a file by reading it using the async fs.promises.readFile function with the callback based fs.writeFile function. In the real world, you would never mix fs functions like this because there is no need to. But some libraries like stylus and pug use callbacks, and I use something like this all the time in those scenarios.
const fs = require('fs');
function copyFile(infilePath, outfilePath) {
return new Promise(async (resolve, reject) => {
try {
// the fs.promises library provides convenient async functions
const data = await fs.promises.readFile(infilePath);
// the fs library also provides methods that use callbacks
// the following line doesn't need a return statement, because there is nothing to return the value to
// but IMO it is useful to signal intent that the function has completed (especially in more complex functions)
return fs.writeFile(outfilePath, data, (error) => {
// note that if there is an error we call the reject function
// so whether an error is thrown in the promise executor, or the callback the reject function will be called
// so from the outside, copyFile appears to be a perfectly normal async function
return (error) ? reject(error) : resolve();
});
} catch(error) {
// this will only catch errors from the main body of the promise executor (ie. the fs.promises.readFile statement
// it will not catch any errors from the callback to the fs.writeFile statement
return reject(error);
// the return statement is not necessary, but IMO communicates the intent that the function is completed
}
}
}
Apparently everyone says this is an anti-pattern, but I use it all the time when I want to do some async stuff before doing something that can only be done with a callback (not for copying files like my contrived example). I don't understand why people think it is an anti-pattern (to use an async promise executor), and haven't seen an example yet that has convinced me that it should be accepted as a general rule.

Convert callback to Promise

I want to convert the below callEndPointWrapper to return a Promise instead of callback.
I have tested with the below code, but awaiting on Promise for callEndpointWrapper() the request is timing out.
Am I missing any thing below?
(While debugging I see the request times out on below line of code while awaiting for Promise:
return (function callEndpoint(callback): any {
CallBack:
function callEndPointWrapper(): any {
return function callEndpoint(callback) {
try {
// gRPC call
client[methodName](req, options, callback);
}
catch (err) {
callback(err);
}
};
}
const result = yield callEndpointWrapper();
// I get the correct result from callback above (both on success or error)
Promise:
function callEndPointWrapper(): Promise<any> {
return new Promise( (resolve, reject) => {
return (function callEndpoint(callback): any {
try {
// gRPC call
resolve(client[methodName](req, options, callback));
} catch (err) {
reject(callback(err));
}
});
});
const result = await callEndpointWrapper();
// Above request times out.
The desired result of callEndPointWrapper seems to be a function (callEndPoint) that does some asynchronous work which you can further invoke to do something.
In your callback approach you are producing this callEndPoint function.
----> callEndPointWrapper returns callEndPoint which does async work.
In your promise based approach on the other hand, you are attempting to produce the result of callEndPoint rather than return callEndPoint itself. In fact, callEndPoint is never invoked within the Promise constructor.
----> callEndPointWrapper returns a promise which never resolves and internally creates the callEndPoint function that does nothing because it's never invoked.
It's important to note that a single call to callEndPointWrapper is not async. The actual async part (assuming client methods are async) happens in callEndpoint so an async invocation for your callback-based approach would be something like:
callEndPointWrapper()(
function callback(responseFromEndpoint) {
// ...
}
)
// or something like
let caller = callEndPointWrapper();
caller(function callback(responseFromEndpoint) {
// ...
});
From this follows that the promise based approach would require two invocations as well:
await callEndPointWrapper()(); // call a wrapper which returns a function which returns a promise
A functionally equivalent (in terms of the produced result) promise-based code for your callback code is the following:
function callEndPointWrapper(): any {
return function callEndpoint() {
return new Promise((resolve, reject) => {
try {
client[methodName](req, options, (err, result) => {
if (err) return reject(err);
resolve(result);
});
} catch (err) {
// Note: this rejection will only happen on any sync errors
// with the above code, such as trying to invoke a non-existing
// method on the client. This type of error is a programmer error
// rather than an operational error in the system so you should
// consider if such errors should even by caught by your code.
reject(err);
}
});
};
}
However, this leads to the question of what's the point of having the wrapper function at all if you're not passing any config options to be available in the closure of the callEndpoint function?
Based on your example usage, all you need is the callEndpoint method.
It comes to mind that you might be using something like co which allows you to yield functions (thunks) and invokes them internally with a callback.
So when you do
yield callEndpointWrapper()
you are actually calling
yield function callEndpoint(callback) {
// ...
}
And then co does some magic under the hood for you.
This is a pattern that is deprecated by co and is overall ill-advised to use. Not to mention that it's very confusing (and ugly IMHO) behavior that requires knowledge of the particular library.
To use promises with co, you don't need the wrapper function. Just yield or await the result of calling callEndPoint (the same one from my promise example above), which will be a promise.
yield callEndPoint()
// or
await callEndPoint()

Raise exception from async failure in JavaScript Promise

When some asynchronous call within my Promise definition throws an Error, I can't seem to bubble it up to my catch handlers.
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('about to fail!');
throw new Error('async FAIL');
}, 1000)
throw new Error('synchronous fail!');
}).catch(function(err) {
console.log('catch1!!!');
throw err;
});
promise.catch(function(err) {
console.log('CAUGHT!');
console.log(err.stack);
});
This code effectively catches the synchronous failure. But, is there any way to catch the async failure within the Promise rubric? Am I missing something fundamental about how I'm trying to use promises?
You can't catch exceptions thrown asynchronously with promises - or in any other way except the global platform error handler. Promises are not magic of course.
That's why you have to perform promisification at the lowest level possible - since promise handlers implicitly wrap calls with try/catch.
It's possible with closures if the async code has success/failure callbacks. The issue is keeping a reference to the resolve/reject functions in a different call stack:
function mySuccessCallback(resolve) {
return function() {
resolve();
};
}
function myFailureCallback(reject) {
return function() {
reject();
};
}
var promise = new Promise(function(resolve, reject) {
var thirdPartyTool = new ThirdPartyTool();
thirdPartyTool.executeAsync(
mySuccessCallback(resolve),
myFailureCallback(reject));
});
If there aren't success/failure callbacks then how can your code possibly know when theirs has completed an asynchronous action? unless of course your promise object is only promising that the async call will be executed, and nothing more, then it doesn't matter when the async action finishes. But the only way to tell if another bit of code has thrown an exception is to use the global error handler (https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror), because as another person said it's bubbling up in a totally different call stack. But even then how can you generically guarantee that the error being caught is caused by that particular async call and not another?

Categories

Resources