With promises, is it bad practice to leave out the use of reject? If I just want to return a Promise to ensure this function is run in it's entirety and not block whatever follows this function.
function testFunc() {
return new Promise((resolve,reject) => {
// do stuff
resolve(true);
})
}
testfunc().then(() => { // additional stuff});
If there is a chance of the do stuff throwing an error, then you should make sure to use reject and call it in case there's an error, so that the caller can see that there was a problem and (hopefully) handle it appropriately. For example, you wouldn't want to do:
function testFunc() {
return new Promise((resolve,reject) => {
getApi((error, result) => {
resolve(result);
});
});
}
but instead
function testFunc() {
return new Promise((resolve,reject) => {
getApi((error, result) => {
if (error) reject(error);
resolve(result);
});
});
}
If there's really no chance of the contents of the function erroring (barring logic bugs that could be unknowingly present in any code), then reject won't help you, because you don't really have any place suitable to put it. For example:
function someWeirdPromiseFunctionThatBlocks() {
return new Promise((resolve) => {
for (let i = 0; i < 1e5; i++) {
}
resolve();
});
}
Note that if you just want to:
not block whatever follows this function.
then just returning a Promise won't accomplish that, because the Promise constructor will run all of its code synchronously until it encounters something asynchronous (like getApi). To avoid blocking the running of sync code below, use Promise.resolve or setTimeout, eg:
function testFunc() {
return Promise.resolve()
.then(() => {
// do other stuff
});
No, its not necessary at all. reject is meant to be used in a similar way as one might use throw - it's for errors that happen.
In fact, if you're familiar with javascript's async/await syntax, this async function...
async function doSomething() {
throw new Error('Oh no!')
}
...roughly translates to this:
function doSomething() {
return Promise.reject(new Error('Oh no!'))
}
which shows how reject is intended to be used wherever you might normally throw an error.
Here's an example promise that I use often where there isn't any "reject" that would make sense:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
wait(1000).then(() => console.log('done'))
Many others such examples exist.
Related
Written below is an example of my code I usually do.
What I did is..
Add try-catch for a promise function with async-await.
Do no add try-catch for a promise function without async-await.
What I want to know is my code is alright, and it is anti-pattern or not.
Thank you in advance.
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
What I want to know is my code is alright, and it is anti-pattern or not.
The first one will work properly, but is an anti-pattern.
The second one will not work properly.
Neither of the code blocks you show is the recommended way to do things because they are both needlessly wrapping an existing promise in another manually created promise. This is referred to as an anti-pattern. This first version will actually work properly, but it contains a bunch of useless code (thus making it an anti-pattern) and with a little more complexity in the function, it's very easy to make coding mistakes (which is why, in addition to the useless code it contains, its an anti-pattern).
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
It can instead be this:
const readStatusAll = data => {
return CoreStatus.findAll();
}
The caller will receive the promise as the return value and can then use either .then() and .catch() or await and try/catch. Since you aren't doing anything with the error, other than propagating it, you don't need to catch the error locally - you can just let it propagate back to the caller.
Your second version is just not correct at all:
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
Because you're not paying any attention to any asynchronous return from CoreStatus.findAll() so you will resolve this manual wrapper promise long before the database call is actually done. In fact, this isn't even legal code as you have improper bracing.
Perhaps you meant to call resolve(category) in some callback or .then() handler associated with CoreStatus.findAll(). But, even if you did that, you still wouldn't be propagating errors back to the caller. This is not the way to do things.
I'm new to promises, so apologize for the newbie question. Inside my function, I have the following lines (middle of the function):
...
const retPromise = await buildImgs(file, imgArray);
retPromise.then(async function () {
console.log("completed build imgs");
...
My assumption was that the "then" from the promise would not execute until the await was completed. but alas, it is acting like sync code, and the retPromise evaluating the "then" before the buildImgs is completed (as measured by my console.log flows). The result is an undefined retPromise.
please help...what am I missing in the concept?
OK: after feedback, let me explaing further my question:
I am trying to understand this async/sync flow and control concept:
const retVal = somefunc();
console.log(retVal);
const retVal = await somefunc();
console.log(retVal);
in the first case, if I understand sync / async code correctly, then I should have a possibility that retVal is undefined when the console.log finds it...
in the second case, I thought it would stop flow until that point that somefunc() completes, then the flow would continue. However my reading seems to indicate it will still try to run the console.log message as a parallel thread. So this leads me to believe I would need to put the console.log inside of the .then after somefunc. Which leads me to promises. So I made a promise return, which I see happening.
However, the .then, as in my original post code, seems to post the console message "completed build imgs", before code inside my buildImgs completes (measured by time I know the function to take, and also console messages inside the buildImgs to help me with sequencing)
so it seems to me I am still missing a fundamental on how to block flow for async code. :(
When you use await construction the script waits until the promise resolves and return to your retPromise value from this promise.
So in this case better to choose one. Remove await and keep then, or keep await and use retPromise value.
Assuming that buildImgs is actually returning a promise (example)
const buildImgs = (file, imgArray) => {
return new Promise((resolve, reject) => {
try {
// ...
resolve()
} catch (err) {
reject(err)
}
})
}
When you call await on a promise its already waiting for the promise to complete, if you remove the await on the call then you can use .then
there are two ways to write promise handlers, the older way looks like this
buildImgs(file, imgArray)
.then(() => {
console.log('im done')
})
.catch(err => {
console.error(err)
})
and the newer syntax
// you must declare "async" in order to use "await"
const asyncFunction = async () => {
try {
await buildImgs(file, imgArray)
console.log('im done')
} catch(err) {
console.error(err)
}
}
asyncFunction()
if you need the return value from the promise, then you would assign it to a variable
const ineedResponse = await buildImgs(file, imgArray)
console.log(ineedResponse)
// or
buildImgs(file, imgArray)
.then(ineedResponse => {
console.log(ineedResponse)
})
.catch(err => {
console.error(err)
})
My code looks like this,
export function handleLogin(window,userData){
return (dispatch) => {
Meteor.call('checkUserLogin',userData,
(error,result)=>{
if(result.isLogin && !error) {
Meteor.call('SOMECALL',SOMEDATA (e,r)=>{
if(!e) {
async function getData(){ return await getAdminUgData();}
getData()
.then((d)=>{console.log('resolve!!');})
.catch((e)=>{console.log('!!reject'); });
}
});
});
}; }
the getAdminUgData is,
export function getAdminUgData(){
return new Promise((resolve, reject) => {
Meteor.call('adminGetUserGroupData', (e,r)=>{
if(e) reject(new Error('error'));
else resolve(r);
});
});}
I am supposed to print out 'resolve' only because the resolve(r); is confirmed being called in getAdminUgData. But the confusing/weird reality is that 'resolve!!' is printed and after that, '!!reject' is also printed. And I completely have no ideas about this. Any suggestions are welcome; thanks.
No, it's absolutely impossible for the same promise to both reject and fulfill - and so it will never happen that both callbacks to .then(…, …) are called. It is however totally possible that both a .then(…) and a .catch(…) callback are called when chained - notice that this doesn't seem to be the case in your example, it seems there's something else going on.
I know that there are answers out there but I didn't find a specific answer to my actual question.
Currently I use the following pattern a lot :
class A
{
getLocation()
{
return Promise.reject(2222222)
}
async a()
{
try
{
var loc = await this.getLocation();
alert(loc)
}
catch (e)
{
alert("catch 2")
}
}
}
new A().a();
Result : "catch 2"
Event If I throw an error in getLocation :
getLocation()
{
throw Error("ffffff")
}
- I get the same result - which is OK.
So where is the problem ?
Well as you know , an error which is thrown asynchronously-non-promised is a different beast :
So this code won't be catched at all:
getLocation() //bad code from a third party code , third party code
{
return new Promise((v, x) => setTimeout(() =>
{
throw Error("ffffff")
}, 100))
}
Question :
Regarding the fact that I want to catch errors - is there a better pattern for capturing this ?
Sure I can do :
window.onerror = function () { alert(4)}
But that would be not in order as the flow of .catch(...) or catch(){} , and I won't be able to do actions regarding that specific action that caused error.
Full disclosure:
No real life scenario. Learning purpose .
an error which is thrown asynchronously-non-promised is a different beast
Yes. And it must be avoided at all costs. Therefore, never put business code (including trivial things like property accesses) in asynchronous non-promise callbacks. It could throw! It should be obvious that JSON.parse can fail, that a property access can throw when the "object" is null or undefined or a getter is involved, or that a loop can fail when the thing that was supposed to be an array has no .length.
The only things that are allowed as asynchronous non-promise callbacks are resolve, reject, and (err, res) => { if (err) reject(err); else resolve(res); } (and maybe a variadic version for weird APIs with multiple arguments).
So rewrite the bad code to
async getLocation() {
await new Promise(resolve => setTimeout(resolve, 100));
throw Error("ffffff");
}
or
getLocation() {
return new Promise(resolve => setTimeout(resolve, 100)).then(res => {
throw Error("ffffff");
});
}
and when it's third-party code make them fix it, make an upstream merge request of your fix, or if those don't work abandon that party.
is there a better pattern for capturing this?
Well, domains (in node) were supposed to solve this problem of non-locality of asynchronous (uncaught) exceptions, but they didn't work out. Maybe some day, zones with better native language support will replace them.
The errors should be caught in place where they occur.
This kind of code code is incorrect and should be fixed in-place:
getLocation() //bad code from a third party code
{
return new Promise((v, x) => setTimeout(() =>
{
throw Error("ffffff")
}, 100))
}
If this is third-party code, it can be forked or patched.
Exceptions can be tracked globally by onerror, as the question already mentions. This should be used only to notify a developer of existing errors, not to handle them in normal way.
unhandledrejection event can be used for same purpose to notify about unhandled rejections in promises. It won't be able to handle the error in the snipped above because it is thrown inside setTimeout callback and doesn't result in promise rejection.
I guess the basic usage would be like this:
class A {
getLocation(x) {
return new Promise((resolve, reject) => setTimeout(() => {
// a lot of async code
try {
//simulate unexpected exception
if (x === 2) {
throw ("code error");
}
if (x) {
resolve('success');
} else {
reject('conditional rejection');
}
} catch (ex) {
reject(ex);
}
}, 1000));
}
async a(x) {
await this.getLocation(x).then((loc) => console.info(loc)).catch((e) => console.error(e));
}
}
let o = new A();
o.a(2);
o.a(0);
o.a(1);
The rejection of the Promise is not necessarily an code Exception as well as the code Exception should not necessarily trigger a Promise rejection.
There seems something inherently wrong with having to define a Promise's callback as asynchronous:
return new Promise(async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
This is apparently an antipattern and there are coding problems which can arise from it. I understand that it becomes easier to fail to catch errors here, even when placing await statements inside try/catch blocks.
My first question is, what's the best way to code something like this, when one wants to forward a Promise with different resolve/reject values? With then/catch? I.e.
return new Promise((resolve, reject) => {
somethingAsynchronous().then(value => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}); // errors would now be propagated up
});
Or do you just take it out the Promise constructor altogether as suggested here?
async function outerFunction() {
const value = await somethingAsynchronous();
return new Promise((resolve, reject) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
}
But what if you have several await statements in the outerFunction(), i.e. a linear code block calling several asynchronous functions. Would you then have to create and return a new Promise every time?
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
return 'Nope. Try again.' // another reject case
}
const value = await somethingAsynchronous();
// ...
}
I have the feeling that I'm making this more complicated than it should be. I'm trying to avoid nesting callbacks/chaining then/catch blocks without creating more problems in the future.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
You do this:
async function outerFunction() {
const value = await somethingAsynchronous();
if (value === something) {
return 'It Worked!';
}
throw Error('Nope. Try again.');
}
Using async wraps the result of outerFunction with a Promise.
If you want that wrapping promise to resolve to something, just return it from the async function. If you want the wrapping promise to be rejected, throw an error inside the async function.
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
throw Error('Nope. Try again.');
}
const value = await somethingAsynchronous();
// ...
}
new Promise(async (resolve, reject) => { ... }) is relatively new antipattern. It results in creating 2 promise objects instead of 1, uncaught errors that happen inside constructor cannot be caught with try..catch and result in unhandled rejection.
Considering that promise asynchronous code can be handled with async..await, current use case for Promise constructor is non-promise asynchronous code, e.g.:
new Promise(resolve => setTimeout(resolve, 1000))
When Promise constructor contains synchronous code or involves other promises, a promise should be constructed with async function. A drop-in replacement is async IIFE:
return (async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return 'It worked!';
} else {
throw 'Nope. Try again.';
}
})();
If the need for Promise constructor still presents when being used together with async, Promise constructor should be moved down in hierarchy so it won't wrap any async function.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
async function isn't just a function that is executed asynchronously, it returns another promise that is supposed to be utilized - or at least handled with catch. Promise isn't supposed to utilize a promise that is returned from constructing function.
The constructor can resolve on same tick and doesn't necessarily have to be asynchronous.
Promise.resolve(1);
is similar to
Promise(resolve => resolve(1))
and not to
Promise(resolve => setTimeout(() => resolve(1)))
You can also chain the promises yourself by simply doing this:
return new Promise((resolve, reject) => {
somethingAsynchronous().then((value) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}, (error) => { reject(error); });
});
I've been using this for some time and it works perfectly for me.