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
I'd like to make a function what "wrap" Promise and:
Do something when the promise is invoked (1)
Do something when the promise is resolved, but retain original value (2)
Do something when the promise is rejected but retain the error (3)
I'm not very confident with promises... here is what I may do:
const wrap = (promiseToWrap) => {
return new Promise((resolve, reject) => {
console.log('loading...'); // (1)
promiseToWrap
.then(res => {
console.log('show success toast'); // (2)
resolve(res);
})
.catch(err => {
console.log('show error toast') // (3)
reject(err);
});
});
}
Assuming this solution is right, what's the difference with chaining a new Promise (the one that do "something") with the passed promiseToWrap?
You don't need to create a new promise, you can chain the existing one. To keep the return value and error, just return the value and re-throw it respectively:
const wrap = promiseToWrap => {
console.log('loading...'); // (1)
return promiseToWrap.then(
res => {
console.log('show success toast'); // (2)
return res;
},
err => {
console.log('show error toast') // (3)
throw err;
}
);
}
Better though, write it with async:
const wrap = async (promiseToWrap) => {
console.log('loading...'); // (1)
try {
const res = await promiseToWrap;
console.log('show success toast'); // (2)
return res;
} catch (err) {
console.log('show error toast') // (3)
throw err;
}
}
Alternatively, you can simply "latch onto" the promise to trigger additional callbacks, and return the original promise unchanged:
const wrap = promiseToWrap => {
console.log('loading...'); // (1)
promiseToWrap.then(
res => console.log('show success toast'), // (2)
err => console.log('show error toast') // (3)
);
return promiseToWrap;
}
What you wrote is equivalent to
const wrap = (promiseToWrap) => {
console.log('loading...'); // (1)
return promiseToWrap
.then(res => {
console.log('show success toast'); // (2)
return res;
})
.catch(err => {
console.log('show error toast') // (3)
throw err;
});
}
or more succinctly
const wrap = async (promiseToWrap) => {
console.log('loading...'); // (1)
try {
const result = await promiseToWrap;
console.log('show success toast'); // (2)
return result;
} catch (err) {
console.log('show error toast') // (3)
throw err;
}
};
This probably isn't what you want.
Note that Promise callbacks are immediately invoked, by the time the Promise to wrap is passed to your function it is already "loading" and may well be resolved.
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'));
})
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 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.
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'))