chaining promise (bluebird) - javascript

I am using nodeJs and bluebird. i have methode to check for parameters validity, so i create a module where i have (see code below) and all of them are promises.
exports.validateSpeciality = validateSpeciality;
exports.validateGovernorate = validateGovernorate;
exports.validateCities = validateCities;
In my controller (see code below) i always get the first promise result in the then containing "res.send(results)"
validator
.validateSpeciality(speciality)
.then(validator.validateGovernorate(governorate))
.then(validator.validateCities(governorate, cities))
.then(Doctor.searchBySpecialityAndByCities(speciality, cities))
.then(function (results) {
console.log(results);
res.send(results);
})
.catch(function (error) {
console.log(error);
res.status(400).send(error);
})
Can someone explain to me why it is not working in this way? even if one of the promise is rejected it always execute the last then and don't go to catch.

Currently in your Promise chain, you don't use the results of the previous function calls. You run the next promise, when the previous was not resolved. So, even one of the functions: validateGovernorate, validateCities, searchBySpecialityAndByCities rejected with error, the final then will be called anycase.
To fix that, run the next function, when the previous promise is resolved:
validator
.validateSpeciality(speciality)
.then(function() {
return validator.validateGovernorate(governorate);
})
.then(function() {
return validator.validateCities(governorate, cities);
});
.then(function() {
return Doctor.searchBySpecialityAndByCities(speciality, cities);
})
.then(function(results) {
console.log(results);
res.send(results);
})
.catch(function (error) {
console.log(error);
res.status(400).send(error);
});
Also, check that you haven't catch blocks in all the functions: validateGovernorate, validateCities, searchBySpecialityAndByCities. And if you have throw an error there or reject with error.
Check this article there is very good desribed how promises work.

Related

How to fix stop promise returned in .catch beeing processed in the .then block of the preceding promise. Ie .catch should stay in .catch

Then a promise calls another promise and the inner promise returns from catch the outer one processes in the .then block
I have searched here and google generally.
Tried to use a simple try.. catch. But will not work with calling a promise
assignResolver(data)
.then(function(resp) {
console.log("map then");
console.log(resp);
})
.catch(function(err) {
console.log("map catch");
console.log(err);
});
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
} )
.catch(function(err) {
console.log("in api then block");
console.log(err);
});
}
Just throw the error again, when inside the inner catch, and it'll be handled in the outer catch, and not the outer .then:
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
} )
.catch(function(err) {
console.log("in api then block");
console.log(err);
throw err;
});
}
But this is a bit strange to do - it usually makes more sense to catch in only one place. For example, if assignResolver really needs to be able to do something specific when encountering an error, and your outer caller needs to be able to do something else when encountering the error as well, having two catches can be an option, but in most cases, you can just have a single catch wherever the error can be handled properly.
Here, unless assignResolver itself needs to do something on encountering the error, leave out its catch entirely:
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
})
}
.catch is meant to handle the error, so afterwards the promise chain will continue regularily with the next .then. To continue with the next .catch you have to rethrow the error from inside the .catch or return a rejected promise.

Uncaught (in promise)

I know the problem is usual. I'm using es6 promises, and I have multiple layers.
On runtime, when I don't catch a promise, I have Uncaught (in promise) in my console. But the fact is that I do catch it lower in my code.
Fast simplified example :
LoginApi.js
var loginDaoCall = loginDao.login(username, password);
loginDaoCall
.then(function (res) {
store.dispatch(loginSuccess());
log.log("[loginApi.login] END");
})
.catch(function (err) {
store.dispatch(loginFail());
errorUtils.dispatchErrorWithTimeout(errorLogin);
log.log(err);
});
return loginDaoCall;
loginContainer.js
loginApi.login(user, password).then(() => {
// Change here instead of in render so the user can go back to login page
this.props.history.push(baseUrlRouter + "test");
}); // <- Error here cause I don't CATCH the promise, but I do catch it in my loginapi.js
I know that I could catch doing nothing, but eh. I could also do the history push thing in my API layer, but it is not its responsibility.
How can I avoid the error in my console? Is there a way? I'm even thinking about leaving it like this.
Your problem is that you were returning the rejected loginDaoCall, not the promise where the error was already handled. loginApi.login(user, password) did indeed return a rejected promise, and even while that was handled in another branch, the promise returned by the further .then() does also get rejected and was not handled.
You might want to do something like
// LoginApi.js
return loginDao.login(username, password).then(function (res) {
store.dispatch(loginSuccess());
log.log("[loginApi.login] END");
return true;
}, function (err) {
store.dispatch(loginFail());
errorUtils.dispatchErrorWithTimeout(errorLogin);
log.log(err);
return false;
}); // never supposed to reject
// loginContainer.js
loginApi.login(user, password).then(success => {
if (success) {
// Change here instead of in render so the user can go back to login page
this.props.history.push(baseUrlRouter + "test");
}
});
It sounds like you have an error in your catch block. When the error is thrown there is no 2nd catch block to catch the error in the 1st catch block.
To fix it ...
.then(function (res) {
// some code that throws an error
})
.catch(function (err) {
// some code that throws an error
})
.catch(function (err) {
// This will fix your error since you are now handling the error thrown by your first catch block
console.log(err.message)
});

Add reject/resolve to catch/success

I use the following code and I wonder for best practise usage if I should add reject to
this promise inside the catch?
run: function (req, res) {
if (req) {
return this._un(req).then(function() {
return proce.restart().then(function() {
return res.status(200).end("sucess");
//Here should I use reslove
});
}).catch(function(err) {
return res.status(500).send("error: " + err);
//Here should I use reject???
});
}
else {
return new Promise(function(resolve, reject) {
reject("No application content found");
});
}
}
};
You don't "add reject" to a promise. A promise is either unsettled, or settled (resolved/rejected).
If req is provided, your code currently returns a promise that will be resolved with the return value of end (if the restart was successful) or the return value of send (if it wasn't), which I believe in both cases is the response object itself (res).
If you want the caller to be aware of whether the restart was successful, then yes, you want to reject the promise instead; with ES2015 promises you can do that by throwing in catch and I assume Bluebird is similar:
.catch(function(err) {
res.status(500).send("error: " + err);
throw err; // Or `throw new Error(err);`, it depends on what `err` is and your convention
})
...or by using Bluebird's Promise.reject (which is also ES2015-compatible):
.catch(function(err) {
res.status(500).send("error: " + err);
return Promise.reject(err);
})
If you don't want the caller to be aware of whether the restart was successful, then don't.

Promise code are read twice

I use the following code to read json file and return a promise
I've two questions
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
return JSON.parse(res);
},
function (err) {
throw new Error("Error :" + err);
}).then(function () {
console.log("test2");
});
});
I use the console log and I see that the console is printed twice
test
test
test2
test2
why its happening and how to avoid it ?
In the place I've put console.log("test2"); I need to invoke event
that the json parse is finished and still return outside the json object (to the caller), when I add the last then it doesn't work(the returned object is undefined),any idea how to do that right?
UPDATE I try like following which it doesn't work...
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
JSON.parse(res); //data parse
}.catch(function (err) {
throw new Error("Error :" + err);
}
).then(function (data) {
obj.emit('ready');
return data;
}))
});
}
UPDATE2 I was able to solve it by simply add new return JSON.parse(res);
Now how should I solve the first issue which method called twice
Like #jaromandaX said, you probably got two *.json files. Try to print out the file name instead and it should become more obvious. In that case, .map is expected to be called twice, once for each file. Otherwise you aren't gonna be able to read and parse two files together.
If you want to get it to converge to a single point after all file reads and parses are complete, then you need to chain another .then after .map. eg.
return globAsync("folder/*.json")
.map(function(file) {
...
})
.then(function() {
obj.emit('ready');
});
EDIT To answer your question in comment. There are a few things you should keep in mind.
Throwing Error inside the promise chain will get caught by the promise and send it into the rejection flow. You may still throw an error if you are interested in getting custom error type or printing stack trace in a desirable way. But most people prefer return Promise.reject(error).
Any rejection in .map will send the promise chain into rejection flow.
Inside the rejection chain, if you want to continue down the rejection flow. You need to return Promise.reject(error), otherwise if you don't return a reject object, you can bring it back into resolve flow.
If you want to want to handle each error individually, you can do something like this:
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.map(function(file) {
return fs.readFileAsync(file, 'utf8')
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(res) {
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});
If you want to handle once for glob and once for file read, then you have to get a bit more creative.
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(files) {
return Promise.resolve(files)
.map(function(file) {
return fs.readFileAsync(file, 'utf8');
})
.catch(function(error) {
// TODO: Handle error once for any read error
return Promise.reject(error);
})
.map(function(res) {
// Judging by your original code, you are not handling
// parser error, so I wrote this code to behave equivalent
// to your original. Otherwise chain parse immediate after
// readFileAsync.
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});

Node.js Q promises then() chaining not waiting

I am trying to refactor the following code to avoid the callback hell, transforming it into:
createUser(user_data)
.then(createClient(client_data))
.then(createClientPartner(clientpartner_data))
.then(function(data) {
cb(null, _.pick(data,['id','username']));
}, function(error) {
cb(error);
});
As you see, I created a method for each one of the steps:
function createUser(user_data) {
console.log('createUser');
var deferred = Q.defer()
new db.models.User.create(user_data, function(err, user) {
console.log('createUser done');
if (!!err)
deferred.reject(err);
else {
client_data['id'] = user.id;
deferred.resolve(user);
}
});
return deferred.promise;
}
The other methods have identical console.log calls to be able to follow the execution path.
I would expect it to be:
createUser
createUser done
createClient
createClient done
createClientPartner
createClientPartner done
But instead, it is:
createUser
createClient
createClientPartner
createClientPartner done
createUser done
createClient done
Why does are the functions triggered when the previous promise have not been resolved? I expect "then" to wait until previous promise have been resolved or rejected to continue. Am I missing something important about promises?
The problem is you don't pass functions, but the result of function calls.
Instead of
createUser(user_data)
.then(createClient(client_data))
you should have
createUser(user_data)
.then(function(user){
createClient(client_data) // no need for the user ? really ?
})

Categories

Resources