I try to figure out how to proper handle errors in promise chain. There are few promises and one of them throws error. In case of error I would like to terminate function in chain.
The issues I faced with are:
How can we terminate next call from catch block?
How to guarantee execution order then().catch().then() chain? At this
moment I observe catch block is called after execution of both then() functions.
Code sample:
function run() {
test().then(function(data) {
console.log("w3", "Print data: " + data);
});
}
function test() {
return new Promise(function(fulfill, reject) {
ask()
.catch(err => console.log("w3", "Process error from ask: " + err))
.then(reply())
.catch(err => console.log("w3", "Process error from reply: " + err))
.then(function(data) {
console.log("w3", "Finish test");
fulfill(data);
// TODO finish data
})
});
}
function ask() {
return new Promise(function(fulfill, reject) {
console.log("w3", "ask");
// fulfill("Hello");
reject("Cancel Hello");
});
}
function reply() {
return new Promise(function(fulfill, reject) {
console.log("w3", "reply");
fulfill("World!");
// reject("Cancel World");
});
}
That's not how Promise chains work. Promises will only chain correctly if you explicitly return a Promise out of every function invoked in your .then() blocks. If you don't return a Promise out of these functions, the next .then() block is immediately invoked.
You probably want to do something like this:
function run() {
test()
.then(function (data) {
console.log("w3", "Print data: " + data);
});
}
function test() {
return ask()
.then(function (result) {
return reply();
})
.then(function (data) {
console.log("w3", "Finish test");
return Promise.resolve(data);
})
.catch(function (error) {
console.log("Hey an error was thrown from somewhere");
if(error instanceof UnexpectedAskError) {
//handle specific error logic here.
}
});
}
function ask() {
console.log("w3", "ask");
return Promise.reject(new UnexpectedAskError("Ask threw an error"));
}
function reply() {
console.log("w3", "reply");
return Promise.resolve("World!");
}
Notice how the ask function returns a specific type of error? You can do something like that if you need to perform different error handling based on which function threw an error.
To specifically answer the questions you asked:
The above method of chaining promises will prevent the next .then() being called if an error is thrown by the previous function.
The above method of chaining will ensure that your .then() blocks are called in the order specified. As long as every .then() is a function, and every function returns a Promise, either through constructing a new Promise(function(resolve, reject)) or through Promise.resolve or Promise.reject, the chain will execute in the correct order.
You just catch at the end of the chain. If there's any error. It will ignore the other then and go direct to catch.
function test() {
return new Promise(function(fulfill, reject) {
ask()
.then(reply())
.then(function(data) {
console.log("w3", "Finish test");
return fulfill(data); // <= Make sure to return here
// TODO finish data
})
.catch(err => console.log("w3", "Process error from reply: " + err))
});
}
Make sure to return Promise or else it won't catch your error.
fulfill(data); should be return fulfill(data);
Related
I wanted to know why the promise below both call the catch() and then() method.
function testpromise(){
return new Promise((resolve, reject)=>{
reject("Error");
})
}
testpromise()
.catch((err)=>{console.log(err)})
.then(()=>{console.log("Then")})
But why this one doesn't ? (I only moved the .then() method before the .catch() one).
function testpromise(){
return new Promise((resolve, reject)=>{
reject("Error");
})
}
testpromise()
.then(()=>{console.log("Then")})
.catch((err)=>{console.log(err)})
Is this a bug ? It's kind weird that I can't get the catch() method before the .then(). For me putting catch before then allow me to quickly check if I handle possible errors correctly. I have dozens of promises wrote like so and I just noticed that it will also call my .then() method right away, which is not good.
A catch returns a promise so unless you throw or return another rejected promise it will resolve to the next then() in the chain
Following contrived example should help you visualize it
testpromise()
.catch((err) => {
console.log('First catch=', err)
return 'Catch message'; // implicit return value, with no return next then receives undefined
})
.then((fromCatch) => {
console.log("from first catch =", fromCatch)
})
.then(testpromise) // return another rejected promise to next catch
.then(() => console.log('Not called'))
.catch((err) => {
console.log('Second catch');
throw 'New Error'
})
.then(() => console.log('Not called #2'))
.then(() => console.log('Not called #3'))
.catch(err => console.log('Final catch =', err))
function testpromise() {
return new Promise((resolve, reject) => {
reject("Error");
})
}
Consider the following code:
try {
throw new Error("catch me if you can!");
} catch (err) {
// whatever
}
console.log("hi");
Because that is the synchronous equivalent of this:
Promise.reject(new Error("catch me if you can!"))
.catch(_ => { /* whatever */ })
.then(_ => console.log("hi"));
Each time you add a .then or .catch it returns a new promise, it doesn't modify the old one in place. So it's not that the order is wrong, it's that it matters. Otherwise you couldn't do something like this:
fetch(someURL)
.then(response => response.json())
.then(doSomethingWithJSON)
.catch(err => {
handleRequestFailure(err);
console.error(err);
return err;
})
.then(result => {
if (result instanceof Error) {
return fallbackRequest();
}
})
.catch(handleFallbackError);
A .catch() is really just a .then() without a slot for a callback function for the case when the promise is resolved.
MDN Promises Documentation
Think of the .then() function as a chain, each one does something according to the resolved value, and passes the returned value to the next .then() in the chain, or if an error thrown, to the .catch() method.
Hi I am trying to write this function as a promise, I know how to write async functions in es6 but in es5 I am strugling
This is wath I tried:
function getLeagueByID(sportID) {
console.log(sportID);
new Promise(function (resolve, reject) {
RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]);
})
.catch(function (err) {
console.log(err);
});
});
}
And how should I call this function latter?
Three notes:
Whenever you already have a promise (in this case, from RulesService.getLeagueByID) you don't need to use new Promise. Insetad, chain off the promise you already have. (More here.)
Don't swallow errors; either allow them to propagate to the caller so the caller knows what's going on, or report them (not just to the console). Only handle errors at the highest level you can, which is usually an event handler that kicked off the overall process.
You aren't returning anything from the function.
Addressing those, your function should probably look like this, which allows the caller to deal with errors:
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function(results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]); // If this returns a promise or
// value you should return, add
// `return` in front of it
});
});
}
But if you don't expect the caller to handle errors, then:
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function(results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]); // If this returns a promise or
// value you should return, add
// `return` in front of it
})
.catch(function (err) {
// Do something other than just `console.log` here, in most cases; for instance, show an
// error to the user
});
});
}
And how should I call this function latter?
That depends on what's calling the function. As with the above, if it's something that's expecting its caller to handle errors, then:
return getLeagueByID(someSportId);
If it's an event handler or similar, and so doesn't expect its caller to handle errors, then:
getLeagueByID(someSportId)
.catch(function (err) {
// Do something other than just `console.log` here, in most cases; for instance, show an
// error to the user
});
As RulesService.getLeagueByID returns a promise, you can just return that.
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]);
})
.catch(function (err) {
console.log(err);
});
}
To call it
getLeagueByID(id)
.then(function(){HANDLER CODE});
function getLeagueByID(sportID) {
console.log(sportID);
return new Promise(function (res, rej) { //you need to return here
RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
res(getStatistics($scope.leagueById[0])); //resolve
return; //return to avoid unintended side effects
})
.catch(function (err) {
console.log(err);
rej(err) //need to reject
return; //return to avoid unintended side effects
});
});
}
A promise is just like an async method.
To use this, you can
await getLeagueByID(sportID)
in an async method, or you can use
getLeagueByID(sportID).then(r=>console.log(r)).catch(e=>console.log(e))
essentially 'forking' the current thread.
It's not much I discovered Javascript Promise.
However I found out a behaviour I couldn't understand concerning the nesting of (new or returned) Promise inside Promises
That's the background (extPromiseX is a third-party function returning Promise):
Case 1:
function myAction(param) {
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
});
}
// Main Process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('Exit with error', error);
});
Now, as expected, I got from the console, in
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
4) Process Terminated
Case 2:
function myAction(param) {
return new Promise(function() {
if (itsAllOkWithInputs) {
// Some sync code here
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
})
} else {
throw 'Something went wrong';
}
});
}
// Main process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('3) --> Exit with error', error);
})
In this second case extPromises are executed but the very first Promise remains pending (confirmed by debug). So the console shows:
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
I empirically realized that I had to change myAction function as follow for the code to work:
function myAction(param) {
return new Promise(function(resolve, reject) {
if (itsAllOkWithInputs) {
// Some sync code here
let ep = extPromise1(sql).then(...);
resolve(ep);
} else {
throw 'Something went wrong';
}
});
}
My question:
I thought returning a promise inside another parental one would make
the parent resolving with the results of the child. This is the case
inside the then code block applied to the external promises. Why
this is not valid for the new Promise case?
Because .then is meant to chain promises. You can return a Promise from inside the then callback, and then itself will return a new Promise.
The Promise constructor is supposed to construct a Promise from an underlying callback. If you return a Promise from inside a Promise constructor, you are doing something wrong conceptually. And that's why it does not work.
function myAction(param) {
if (itsAllOkWithInputs) {
return extPromise1(sql).then(...);
} else {
return Promise.reject('Something went wrong');
}
}
// OR
async function myAction(param) {
if (itsAllOkWithInputs) {
await extPromise1(sql);
} else {
throw 'Something went wrong';
}
}
new Promise(function() {
You are not using neither resolve nor reject arguments (you actually even haven't declared them), so the promise will never get resolved or rejected. All other stuff is irrelevant.
If your function returns promise, you have to simply call it and return a result. If you don't know whether the function return promise or just a value, you can wrap it's call into Promise.resolve, but not in new Promise.
pardon me for asking such a beginner level question, because I not satisfied with answers titled with same question. Basically I need a promise return type for my function after performing some packages function which also returns promise.
myquest.js
module.exports = somefunction = (data){
//performs some processing with data
somePackagePromiseFunc() //return type promise
.then((data) => {
console.log(data);
return new Promise.resolve(data);
}).catch( (err) => {
console.log(err);
return new Promise.reject(err);
});
}
mymain.js
var somefunction = require('myquest');
somefunction(data).then((data) => {
console.log('job done with data ' + data);
}).catch(() => {
console.log('we messed with error: ' + err);
})
Help me to understand my mistake.
The simplest fix to your code is
Fix the syntax in the first line
return something, in this case a Promise, in that function
is somePackagePromiseFunc a function? then call it
fix the return value in .then/.catch
You'll end up with
module.exports = function (data) {
//performs some processing with data
return somePackagePromiseFunc().then((data) => {
console.log(data);
return data;
}).catch( (err) => {
console.log(err);
throw err;
});
}
Note about some of your code:
return new Promise.resolve(data);
Promise.resolve is not a constructor, so remove new would make it
return Promise.resolve(data);
However, you're inside a .then, whatever you return is a Promise that resolves the the value returned in .then - so, no need to wrap it in Promise.resolve at all - so that's why you only need
return data;
Similarly for .catch, except to return a rejected promise, you throw instead of return - though technically you can
return Promise.reject(err);
Note, no "new", because it's also not a constructor
I have a system that wants to create many folders in dropbox using the api, however i appear to attempt to create all the folders at once which causes errors to be generated, stating that i am performing too many write operations from dropbox.
My code is as follows and first uses reduce to create multiple promises which i thought were chained. In these promises the function add is called, which uploads the case to mongodb and then creates a dropbox folder for it, however this results in throttling errors..
bulkAdd: function (req, callback) {
issues = []
i = 1
req.reduce((promise, audit) => {
return promise.then(_ => this.add(audit, function(err,data){
if (err){
console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
}
}));
}, Promise.resolve()).catch(error => {console.log(error)});
},
add: function (req, callback) {
delete req.status
var audit = new Audit(req);
if (req['status_value'] != undefined && req.status_value != ''){
console.log(req['status_value'])
audit.status = [{
status_value : req['status_value'],
status_notes : req['status_notes'],
status_date : req['status_date'],
}]
}
audit.save(function (err, data) {
if (err) {
callback(err, data)
}
else {
return dropbox_functions.createFolder(data.ui)
.then(response => {console.log(response)}, error=> {console.log('\n\n\n',error.error.error)})
.catch(error => console.log(error))
}
});
},
So the problem in your current question comes from the fact that your add function doesn't return a value, I only see it returning undefined.
If a promise returns something else than a promise in its then / catch block, it will use this input for the following function, and it will not wait for any internal processes to run through before finishing
If inside your then / catch blocks, you would return a promise, it would wait before continuing to the next then block, thus handling your requests sequential.
Now in your current code, I believe the easiest would be to handle the resolve inside your reduce, since you seem to be stuck with your callback handle already.
req.reduce((promise, audit) => {
return promise.then(_ => new Promise(
function( resolve, reject) {
this.add(audit, function(err,data){
if (err){
console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
reject( err );
return;
}
resolve( data );
});
})
);
}, Promise.resolve()).catch(error => {console.log(error)});
in this case, the promise would either reject or resolve itself. I have chosen to submit err and data respectively, so this would be handled in the end in case an error occurs, and so that you can have the last data that got saved successfully
To properly chain you should make sure that you return a promise object.
Your reduce in the end creates a promise chain something like this.
Promise.resolve()
.then(() => {
console.log('setting up first timeout');
setTimeout(() => {
console.log("1 wait");
}, 2000);
})
.then(() => {
console.log('setting up second timeout');
setTimeout(() => {
console.log("2 wait");
}, 2000);
})
.catch(err => console.log("error", err));
If you run it, you'll see that it does not wait for one promise to end before moving down the chain.
Whereas, in the second example it waits for the first one to complete because a Promise object is returned by the first promise.
Promise.resolve()
.then(() => {
return new Promise(function(resolve, reject) {
console.log('setting up first timeout');
setTimeout(() => {
console.log("1 wait");
resolve();
}, 2000);
});
})
.then(() => {
return new Promise(function(resolve, reject) {
console.log('setting up second timeout');
setTimeout(() => {
console.log("2 wait");
resolve();
}, 2000);
});
})
.catch(err => console.log("error", err));
The difference is the first example is not returning Promise object. So, if you make sure each success handler returns a Promise object which is resolved only after your add function is done executing, you should be fine.
Unless there is a good reason for bulkAdd() to be written in nodeback style, you will find it more convenient to return a promise. The need for a nodeback will disappear and .reduce() will sit much more comfortably inside the function.
Using .reduce() and preserving nodeback style is possible but cumbersone, as it involves a rather ugly double-shuffle from nodeback to promise, and promise back to nodeback.
Assuming you are free to adapt the caller(s) to accept a returned promise, the code would be something like this :
'bulkAdd': function(req) {
return req.reduce((promise, audit) => {
return promise.then(_ => this.add(audit));
}, Promise.resolve());
},
'add': function(req) {
delete req.status;
var audit = new Audit(req);
if (req.status_value != undefined && req.status_value != '') {
audit.status = [{
'status_value': req.status_value,
'status_notes': req.status_notes,
'status_date': req.status_date
}];
}
return new Promise((resolve, reject) => { // in-line promisification of audit.save()
audit.save((err, data) => {
err ? reject(err) : resolve(data);
});
})
.then(data => dropbox_functions.createFolder(data.ui));
},
all catching and logging intentionally removed
The detail may differ, depending primarily on :
what data (if any) you want to be delivered to the caller
what you want to happen when errors occur.