Break promise chain when condition is not met [duplicate] - javascript

How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})

You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console

You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.

You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});

Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.

Related

Promise is rejecting even tho it is true

If lunchTime is true lunch object should be logged if false err should be.
The console is logging: Error: OOOOOPs
Even if I try to log the lunch object in the then statement it just logs the error message
My plan was to just manually switch the value of lunchTime to false so that I could test the
resolve/reject part of promises, but it's running the catch part of the code even tho it should be resolving.
const lunchTime = true;
function orderMeSomeFood() {
return new Promise((resolve, reject) => {
if (lunchTime === true) {
let lunch = {
food: "BBQ",
drink: "Zen WTR"
};
resolve(lunch);
}
else if (lunchTime === false) {
const err = new Error('OOOOOPs')
reject(err);
}
}
})
};
orderMeSomeFood().then(() => {
console.log(resolve);
}).catch(() => {
console.log(Error('OOOOOPs'));
})
The problem is actually with this line of code:
console.log(resolve);
which perhaps you meant to be:
console.log("resolved");
instead. The actual resolve variable and value only exists inside the new Promise() executor function, not elsewhere. So, this throws an exception.
In case you didn't realize this, an exception inside a .then() or .catch() handler will trigger the next .catch() handler in the promise chain to get called. So, when the above exception happens inside the .then() handler, that causes code execution to jump to the next .catch() handler.
If you add this debugging:
orderMeSomeFood().then(() => {
console.log("got to .then() handler");
console.log(resolve);
}).catch((e) => {
console.log(e);
});
Then, you will see that it got to the .then() handler and then you will see that the actual error in the catch handler is ReferenceError: resolve is not defined and the line number will point to console.log(resolve) as the offending statement.
A lesson here is to ALWAYS log the actual exception you get in the .catch() because that will usually be a useful hint at to why your code got there.
Here's a runnable version with more logging that shows you the actual flow:
const lunchTime = true;
function orderMeSomeFood() {
return new Promise((resolve, reject) => {
if (lunchTime === true) {
let lunch = {
food: "BBQ",
drink: "Zen WTR"
};
console.log("about to resolve promise");
resolve(lunch);
} else if (lunchTime === false) {
console.log("about to reject promise");
reject(new Error('OOOOOPs'));
}
})
};
orderMeSomeFood().then(() => {
console.log("got to .then() handler");
console.log(resolve);
}).catch((e) => {
console.log("got to .catch() handler");
console.log(e.message, e.stack);
})
That provides this output:
about to resolve promise
got to .then() handler
got to .catch() handler
resolve is not defined ReferenceError: resolve is not defined
at https://stacksnippets.net/js:32:17
So, you can see the follow:
It resolved the promise
It got to the .then() handler.
Inside that .then() handler on the console.log(resolve) line of code, it threw an exception
That sends it to the .catch() handler where it now logs the cause of the error
resolve only exists within the promise, so when you do console.log(resolve); it's throwing an error, which is why you're seeing the OOOOOPs message.
If you want to console.log the lunch variable, you should change your code to:
orderMeSomeFood().then(lunch => {
console.log(lunch);
}).catch(() => {
console.log(Error('OOOOOPs'));
})
const lunchTime = true;
function orderMeSomeFood() {
return new Promise((resolve, reject) => {
if (lunchTime === true) {
let lunch = {
food: "BBQ",
drink: "Zen WTR"
};
resolve(lunch);
}
else if (lunchTime === false) {
const err = new Error('OOOOOPs')
reject(err);
}
})
};
orderMeSomeFood().then(lunch => {
console.log(lunch);
}).catch(() => {
console.log(new Error('OOOOOPs'));
})

Promise.all causes Jest to display UnhandledPromiseRejectionWarning

I have some code which calls Promise.all. It runs OK in the browser with no warnings in the console.
There are 3 functions f1, f2 & f3 all of which return a promise. The code looks like this
Promise.all([
f1(),
f2(),
f3()
]).then((values) => {
resolve({success: true})
}).catch(err => {
reject(err)
})
When I use Jest to test the file containing the above code I see this error.
(node:17177) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 18)
Is this the wrong way to code the above or is it a bug within Jest?
Here's the actual code that I'm using:
getDataFromDatabase() {
return new Promise((resolve, reject) => {
const assessmentUrl = `${this.assessmentUrl}`
http.get(assessmentUrl).then(response => {
if (response.data.record === null) {
Promise.all([
this._getPupilPlacement(),
this._getSurveyQuestions(),
this._getCompetencies()
]).then((values) => {
successState.pupilPlacement = values[0].pupilPlacement
successState.items = values[1].items
successState.formid = values[2].formid
successState.competencies = values[3].competencies
const panels = this.getPanels(values[3].competencies)
successState.panels = panels
successState.numPages = panels.length
successState.itemsAreOverridden = true
resolve(successState)
}).catch(err => {
reject(err)
})
}
else {
resolve(response.data.record)
}
})
})
}
Avoid the Promise constructor antipattern! You were forgetting to handle errors from the http.get(assessmentUrl) promise.
You should be writing
getDataFromDatabase() {
const assessmentUrl = `${this.assessmentUrl}`
return http.get(assessmentUrl).then(response => {
//^^^^^^
if (response.data.record !== null)
return response.data.record;
return Promise.all([
// ^^^^^^
this._getPupilPlacement(),
this._getSurveyQuestions(),
this._getCompetencies()
]).then(values => {
const panels = this.getPanels(values[3].competencies)
return {
// ^^^^^^
pupilPlacement: values[0].pupilPlacement,
items: values[1].items,
formid: values[2].formid,
competencies: values[3].competencies,
panels: panels,
numPages: panels.length,
itemsAreOverridden: true,
};
});
});
}
Explanation:
Calling reject will throw an error. If your top level promise doesn't catch it, then well it's an unhandled promise.
MDN Image src
Solution:
getDataFromDatabase().catch(err=>console.lor(err.message));
Example of a promise that rejects.:
function getDataFromDatabase(){
return Promise.reject(123);
}
getDataFromDatabase()
.then(data=>console.log("Success " + data))
.catch(err=>console.log("Error " + err));
Promise MDN doc
Future recommendation:
For every child promise you seem to be adding a .catch() which isn't needed. As long as somewhere higher up there is a catch, then the promise will be handled.

How do I break a promise chain?

How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.

How do I flatten a nested promise dependency?

I'm using mondora/asteroid through a node app to use Meteor DDP via a promise pattern.
I have the following code I am rewriting from callback style, into a promise style, but am stuck on how to flatten it.
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
asteroid.call('Pony.finish', { params })
// this part feels wrong
.then(res => {
// something else
});
} else {
// nope
}
})
.catch(err => {
console.error(err);
});
There is a second asteroid.call & then inside the first promise response which is a promise. This part feels wrong, like it should be flat and not nested but I'm not sure how to get there.
edit:
Ended up using something like this (still not decided on whether to have the first then check for if length === 1 and potentially immediately reject it. Anyone know what is best practice on that?
asteroid.call('Pony.search', { params })
.then(res => res.length === 1 ? res : Promise.reject())
.then(res => asteroid.call('Pony.finish', { res[0].something }))
.then(res => {
// do something
})
.catch(err => {
// handle the no found
console.error(err);
});
Instead of nesting callbacks, chain promises together with .then()
A note:
I'm not sure what Promise library you are using, but the idea is to return a rejected promise from the first .then() if there is an error, otherwise you return a successful promise. The promise library will then handle the error handling for you, going to the catch block if there is a rejected promise.
asteroid.call('Pony.search', { params })
.then(res => {
res.length === 1 ? return asteroid.call('Pony.finish', { params }) : Promise.reject();
})
.then(res => {
//do stuff here
})
.catch(err => {
console.error(err);
});
edit:
The only issue is when you need to access both of the return values from the promises at the same time. When you flatten out a promise chain you lose access to the results from the previous promises.
You have a few options:
If you don't need the previous result then flatten out the promise chain like I did here
If you do need the previous value
2a. And you don't care about the ordering of execution then use Promise.all([promise1, promise2])
2b. And you do care about the ordering of the execution then you must use nested promises like you originally did.
If promises are nested, there is no difference between promises and callback.
Try changing your code to use promise chain:
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
let p1 = asteroid.call('Pony.finish', { params });
return p1;
}
else {
// nope
}
})
.then (function SomeThingElse(res2){
// something else
// res2 is return value from nested asteroid.call
})
.catch(err => {
console.error(err);
});
Since the first resolve handler returns a Promise p1, the invocation of the next functions in the chain (function SomeThingElse) is deferred until p1 is resolved.
Now expanding this example:
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
let p1 = asteroid.call('Pony.finish', { params });
return p1;
}
else {
// nope
}
})
.then (function SomeThingElse(res2){
// something else
// res2 is return value from nested asteroid.call
})
.then (function AnotherFunc(res3){
})
.catch(err => {
console.error(err);
});
If SomeThingElse returns a Promise, invocation of AnotherFunc is delayed until that promise is resolved.
If SomeThingElse does not return a Promise, AnotherFunc would be invoked immediately with the same parameters as 'SomeThingElse' received. In other words, both SomeThingElse and AnotherFunc are invoked when p1 is resolved.

How to reject a promise from inside then function

This is probably a silly question, but mid promise chain, how do you reject a promise from inside one of the then functions? For example:
someActionThatReturnsAPromise()
.then(function(resource) {
return modifyResource(resource)
})
.then(function(modifiedResource) {
if (!isValid(modifiedResource)) {
var validationError = getValidationError(modifiedResource);
// fail promise with validationError
}
})
.catch(function() {
// oh noes
});
There's no longer a reference to the original resolve/reject function or the PromiseResolver. Am I just supposed to add return Promise.reject(validationError); ?
Am I just supposed to add return Promise.reject(validationError);?
Yes. However, it's that complicated only in jQuery, with a Promise/A+-compliant library you also could simply
throw validationError;
So your code would then look like
someActionThatReturnsAPromise()
.then(modifyResource)
.then(function(modifiedResource) {
if (!isValid(modifiedResource))
throw getValidationError(modifiedResource);
// else !
return modifiedResource;
})
.catch(function() {
// oh noes
});
Use Promise.reject
Like this
function test()
{
return new Promise((resolve, reject) => {
resolve(null)
}).then(() => console.info('yes')).then(() => {
return Promise.reject('hek');
})
}
test().then(() => console.info('resolved')).catch(() => console.info('rejected'))

Categories