Should I use .done after the last promise? - javascript

In react-native, unhandled promises are silent. There used to be an "Unhandled Promises Rejection" warning, but now I don't see it anymore. Someone knows what happen to that?
I've discovered that putting .done() after a promise, will raise an error if the promise was rejected.
Suppose that I have a promise that is suppose to never be rejected, should I put .done after it, just to detect possible errors?
const asyncFunc = async () => {
const a = null;
return a.undefinedField;
}
<Button onPress={() => asyncFunc()} />
<Button onPress={() => asyncFunc().done()} />
I'm using react-native which uses promise (Github).
By done I'm referring the one explained here: Promise API Reference.
There was already a similar question here, but it's 5 years old and the react-native project changes very fast.

Promise does not have done function.
You can catch possible errors in catch and place things to do in regardless of success in finally.
For example:
const asyncFunc = () => {
return new Promise((resolve) => {
throw new Error('Error');
resolve('Result');
})
}
asyncFunc().then(res => {
console.log(res);
}).catch(e => {
console.error(e);
}).finally(() => {
console.log('This will be executed in regardless of success');
});
If you're using async - await, you can use try-catch as you can for other functions:
const asyncFunc = () => {
return new Promise((resolve) => {
throw new Error('Error');
resolve('Result');
})
}
const executeAsyncFunc = async () => {
try {
await asyncFunc();
} catch (e) {
console.error(e);
} finally {
console.log('This will be executed in regardless of success');
}
};
executeAsyncFunc();

Related

Can you return a Promise with a predefined finally?

I have a function that returns a Promise. When I am done using that Promise in the .then() or .catch() blocks, I always want to execute the same cleanup code. My current setup is this:
const promiseWithFinally = () => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
}).finally(() => console.log('finally done'))
}
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'))
What I want to happen is that either then done or catch done get logged first and then finally done. However it seems to get executed in the exact opposite order - when I resolve the Promise after a timeout of 5 seconds finally done gets logged first after 5 seconds and then then done immediately afterwards.
What am I doing wrong or is it possible to do this in general? I know I could just append the .finally() to each individual function call, but since it's always the same I'd like to put it in the function definition.
No it's not possible. Finally is for cleaning up after a given promise, not for its then or catch methods.
What you can do is pass then and catch methods to the function which will be appended before finally:
const promiseWithFinally = (chain) => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
setTimeout(resolve, 1000);
}).then(chain.then, chain.catch).finally(() => console.log('finally done'))
}
promiseWithFinally({
then: () => console.log('then done'),
catch: () => console.log('catch done')
})
Short answer
No it is not possible as you cannot rely on when finally is being ran.
Longer answer and possible solution
Code
const cleanupFunc = () => {
console.log('Cleaning up.');
};
const someAsyncMethod = (thenFunc, catchFunc) => {
new Promise(
(resolve, reject) => {
setTimeout(() => resolve(), 5000);
},
)
.then((...args) => {
try {
thenFunc(...args);
} catch (err) {
}
cleanupFunc();
})
.catch((...args) => {
try {
catchFunc(...args);
} catch (err) {
}
cleanupFunc();
});
};
someAsyncMethod((result) => console.log('Then done'), (err) => console.log('Catch done'));
Explanation
Al though it is not possible to rely on finally, what you can do is write a function that needs to do some asynchronous operation returning a promise. In my example this operation is waiting on a 5 second timeout but his can also, for example, be an asynchronous api call that returns a promise.
The next step would be to add a then and a catch call to the promise the asynchronous operation returns that both begin with a try clause in which you call the callback parameter that belongs to the type of resolve (thenFunc for then, catchFunc for catch) followed by a catch which doesn't do anything and ends by calling the cleanup function. in this way you are certain that whatever happens during running the then or catch callback, the cleanup function is being called.
Assuming you know the rest of the handlers to that promise are going to be attached synchronously, and all actions in the handler are synchronous, it is possible, albeit a little bit hacky.
Simply have the finally handler re-attach itself at the end:
const promiseWithFinally = () => {
const thisPromise = new Promise((resolve, reject) => {
// Rejection example
setTimeout(reject, 200);
}).finally(() => {
setTimeout(() => {
thisPromise.finally(() => console.log('finally done')).catch(() => {});
}, 0);
});
return thisPromise;
};
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'));

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.

Why is reject and throw handled differently from async functions in Promise.all?

I'm given an array of promises that I'm passing to Promise.all, but I was getting Uncaught (in promise) Error and I couldn't understand why. Trying to reproduce the error I've come to the following minimal example.
Promise
.all([
new Promise(async () => {
throw new Error('thrown');
}),
])
.then(results => {
console.log('results', results);
})
.catch(error => {
console.log('error', error);
})
Running that you will see an "Uncaught" message in your console.
The example is fixable by removing the async keyword. But, assuming I'm given the promises from a library I don't have control over, how do I make this work?
Another way to "fix" it is by rewriting it like this
Promise
.all([
new Promise(async (resolve, reject) => {
reject(new Error('rejected'));
}),
])
.then(results => {
console.log('results', results);
})
.catch(error => {
console.log('error', error);
})
Why does this work differently from the throw version? I thought the throw would be handled as a rejection.
EDIT:
Thanks to #CertainPerformance's answer below, my example can be simplified even further, like this
Promise
.all([
new Promise(async () => {
throw new Error('thrown');
}),
])
.then(() => console.log('resolve'))
.catch(() => console.log('reject'));
And then getting rid of Promise.all altogether like so
new Promise(async () => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And since async is pretty much only syntactic sugar around promises, the above can be rewritten like this, and still show the same behavior
new Promise(() => {
return new Promise(() => {
throw new Error('thrown')
});
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
throw inside a promise rejects that promise, so the above can be rewritten like this
new Promise(() => {
return new Promise((resolve, reject) => {
reject('instead of throw')
});
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And all that does, is immediately rejects the promise, and we have an easier way to do that:
new Promise(() => {
return new Promise.reject('instead of throw');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And with that I think I fully understand why things are working the way they do.
EDIT 2:
Here's a longer example that more realistically mimics my actual code
function textPromise() {
return new Promise(resolve => {
setTimeout(() => {
resolve('data from fetch');
}, 2000);
});
}
function fetch(ok) {
return Promise.resolve({
ok,
text: textPromise(),
});
}
async function handleResponse(response) {
const data = response.text;
if (!response.ok) {
const resolvedData = await data;
throw new Error(resolvedData);
}
return data;
}
function post(ok) {
const promise = fetch(ok)
.then(response => handleResponse(response));
return promise;
}
const validatorPromise = new Promise(resolve => {
post(false).then(response => {
if (response === 'data from fetch') {
resolve('validated OK');
} else {
resolve('validated NOK');
}
});
});
Promise
.all([validatorPromise])
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
That example can most certainly be simplified, and I will try to do that. But it shows the same problem I tried to show in my original example.
The only code I have control over for now is the last Promise.all stuff. I'm given an array of "validator promises", and I need to handle when something in there throws an error
EDIT 3:
And here's a simplified version of the example above, but that still retains the async function
async function handleResponse(response) {
throw new Error('resolvedData');
}
function post() {
return Promise.resolve('').then(handleResponse);
}
const validatorPromise = new Promise(resolve => {
post().then(() => resolve('validated OK'));
});
validatorPromise
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
(The async function will be wrapped in a promise by the JS interpreter, so essentially we will still get new Promise(async ..., but it's not as obvious here)
When you pass a function to new Promise, that function is called immediately with resolve and reject parameters. If the function throws synchronously (that is, the function throws before synchronously completing), the Promise will reject, without requiring the resolve or reject parameters (if used) to be called.
When the async function is invoked, it will return a rejected Promise, but it'll still return something. In contrast, a synchronous function which throws an error will not return at all; execution stops, and the Promise.all knows that since execution stopped during execution that it should look for a .catch handler and invoke it.
When neither resolve nor reject parameters are called, and the function doesn't appear to the Promise.all to throw synchronously (in this case, the async function returns a Promise, rather than failing entirely), the Promise will remain unresolved forever. That's what's happening in your first snippet, so neither the .then nor the .catch chained off the Promise.all run.
The same behavior is exhibited with a plain Promise, without the Promise.all:
// Will remain unresolved forever, because the constructor callback successfully completes
// (and returns a rejected Promise, which goes unused)
new Promise(async () => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
// Will throw synchronously, and therefore reject:
new Promise(() => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
The reasonable thing to do is: invoke the Promise constructor with logical paths that always eventually lead to either its resolve or reject parameter being called. (Or you can throw an error, if the Promise constructor callback isn't async)
Given the new code, which can be reduced to:
const post = ok => ok ? Promise.resolve() : Promise.reject();
const validatorPromise = new Promise(resolve => {
post(false).then(response => {
if (response === 'data from fetch') {
resolve('validated OK');
} else {
resolve('validated NOK');
}
});
});
Promise
.all([validatorPromise])
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
The validatorPromise function is broken. It does not catch errors from post - it only works if post does not reject. If you can't change validatorPromise (nor anything it depends on), the library you're using can be considered to be broken, because it does not handle errors. Post an issue on its github, or fork the library and fix it yourself.

How to wrap a Javascript function so all errors are caught including Promise rejections

I want to write a function used to wrap other functions so all errors are caught, including errors generated by a Promise rejection (which normally requires the .catch Promise method).
The goal is to be able to wrap functions so all runtime errors are handled. An example usage is a function we want to run, but that is optional and not part of the core business flow. If there is an error, we want to report it and later fix it, but we do not want it to stop program flow.
It should be able to wrap functions with any number of arguments, and return the same value as the original function, including if the original function returns a promise.
Am I handling all possible cases below? Is there a simpler way to do this?
const catchAllErrors = (fn) => (...args) => {
try {
const possiblePromise = fn(...args);
// Is it a promise type object? Can't use "instanceof Promise" because just
// for example, Bluebird promise library is not an instance of Promise.
if (typeof possiblePromise.catch === 'function') {
return Promise.resolve(possiblePromise).catch((error) => {
console.log('Caught promise error.', error);
});
}
return possiblePromise;
} catch (error) {
console.log('Caught error.', error);
}
};
// EXAMPLE USAGE
// Applying the wrapper to various types of functions:
const throwsErr = catchAllErrors((x, y) => {
throw `Error 1 with args ${x}, ${y}.`;
});
const promiseErr = catchAllErrors((a, b) => Promise.reject(`Error 2 with args ${a}, ${b}.`));
const noError = catchAllErrors((name) => `Hi there ${name}.`);
const noErrorPromise = catchAllErrors((wish) => Promise.resolve(`I wish for ${wish}.`));
// Running the wrapped functions:
console.log(throwsErr(1, 2));
promiseErr(3, 4).then((result) => console.log(result));
console.log(noError('folks'));
noErrorPromise('sun').then((result) => console.log(result));
Don't try to detect whether something is a promise or not yourself. Use the builtin thenable detection of promise resolution. You can use either the Promise constructor to catch the exception:
const catchAllErrors = (fn) => (...args) => {
return new Promise(resolve => {
resolve(fn(...args));
}).catch((error) => {
console.log('Caught error.', error);
});
};
or just go for async/await syntax:
const catchAllErrors = (fn) => async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.log('Caught error.', error);
}
};
(If you used Bluebird anyway, you could also call its Promise.try method for this purpose)

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