I have the following structure and need to simplify it,
Resuable service1 to interact with IndexedDB
function isDbExist (){
// do something , it return either reject or resolve
}
function createDB (){
// do something , it return either reject or resolve
}
function getData (){
// do something , it return either reject or resolve
}
In another service2 I'm injecting this service1 and using the functions as such
function1 (){
service1.isDbExist.then(function(data){
service1.createDB.then(function(data){
service1.getData.then(function(data){
referred.resolve(data);
},function(error){
deferred.reject(error)
})
},function(error){
deferred.reject(error);
})
},function(error){
deferred.reject(error);
})
}
The problem here is the readability of the code is not good, its not easy to debug as which reject function is for which promise. Is their some good way of doing this ? I have read about $q.all but don't this its applicable in this situation.
Exactly, the beauty of promises is that you can chain them as opposed to nesting them like callbacks. You can return another promise when dealing with the previous like so:
isDbExists().then(function(db) {
// do something
return createDb();
}).then(function(db) {
// do something with with the resolved db..
return getData();
}).then(function(data) {
// Do something with data
}).catch(function(error) {
// Uh oh, something went wrong. Deal with error
});
Finally, you deal with the error that may have occurred.
You can chain multiple promises like this:
service1.isDbExist()
.then(service1.createDB)
.then(service1.getData)
.then(function() {
// DB exists, created and data loaded
})
.catch(function(err) {
console.log('Handle error', err); // isDbExist, createDB or getData rejected
});
Check interactive demo to see how success and error data is passed to the next promise in chain and how you have full control over process on each step:
Demo: http://plnkr.co/edit/PM06e8NGJvHKmoJ9C2Lf?p=info
One of the really great things about promises is that they can be chained like this:
function1 (){
return service1.isDbExist.then(function(exists){
if(!exists) return service1.createDB()
})
.then(function() {
return service1.getData('some', 'args')
});
}
Remember that the .then will receive the result of the previous function, like the exists would be a boolean. function1 will also return a promise and this promise will return the resolved/rejected promise from service1.getData.
Related
How do i chain promises sequentially within for loop, i have seen lot of examples on google to do this but i couldn't implement for my case:
i have gone through this link for sequential chaining of Promises.
What I'm trying to acheive:
Promise1: login();
Promise2: sync();
sync function calls another service complete() for an array of elements. These array of elements must be done sequentially.
ServiceA.login().
then(function(response){
ServiceA.sync()
.then(function(response){
})
})
function sync(){
ServiceB.complete()
.then(function(){
var promises = [];
angular.forEach(response, function (value) {
// The below service call doSomething() must be done sequentially for each "value"
promises.push(doSomething(value));
});
$q.all(promises).then(function () {
});
});
})
}
How do I capture the error occuring in each Promise?
Update:
I have tried the approach suggested by #zaptree with the following code:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value)); // problem is here that doSomething function is being called before the first call it is resolved
// doSomething is a http call.
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}
function doSomething(data){
return $http({
method: 'POST',
url: '/api/do',
data: data,
headers: {
"Content-Type": "application/json"
}
}).then(function (response) {
}, function (error) {
});
}
If the response in the near the for each loop has 2 values (valuea, valueb) in it, the code is behaving as follows:
1. calling doSomething(valuea)
2. calling doSomething(valueb) before the above promise is resolved.
Expected behaviour:
after the POST method has succesfully completed by the call doSOmething(valuea), then the another POST call should happend i.e., soSomething(valueb).
Here's what I came up with. You'll need to reduce the array into a single promise.
var results = [...];
var sequentialPromise = results.reduce(function(a, b) {
return a.then(function(){
return doSomething(b);
});
}, $q.resolve());
sequentialPromise.then(function(){...});
So here is an example on how you would do the sequential promises with Q, also some improvements on how to do your promises so you can properly catch errors thrown at any point in your promise chain. You must always make sure to return a promise on any method that uses them. Also avoid pyramid code by not nesting the .then to make your code cleaner:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value));
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}
I think I'm preventing nested queries as much as possible, but I'm honestly not sure. I understand the calls here can all be executed in a single select query, but I did this to simplify the example.
// This example is in TypeScript
// find user
User.find({where:{username:'user'}})
// if found user
.then(function(user) {
return User.find({where:{username:'other_user'}})
// if found other_user
.then(function(other_user) {
// do stuff
return whatever_i_need
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
}
However, this results in nested promises, which is exactly what promises are meant to prevent. Is this the "max depth" recommended for promises, or am I missing something? Is there a better way to chain queries?
Return the nested promise instead of handling it in the inner blocks to flatten the structure.
User.find({where:{username:'user'}})
.then(function(user) {
if (user) { // if found user
// do stuff
return User.find({where:{username:'other_user'}});
}
throw new Error('user not-found');
})
.then(function(other_user) {
if (other_user) { // if found other_user
// do stuff
return whatever_i_need;
}
throw new Error('other_user not-found');
})
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
.then(function(yet_another_user) {
if (yet_another_user) { // if found yet_another_user
// do stuff
return whatever_i_need_again;
}
throw new Error('yet_another_user not-found');
}
.then(function(data){
// do stuff
})
.catch(function(err) { // if anything threw an error at any point in time
// handle the error
}
Note that a resolved promise means a query is successfully done. That's it all about. A successful query does't guarantee results to be returned. Empty result is a valid outcome of resolved promises.
Note also that the return value from a resolve or reject callback will be wrapped with a resolved promise, and then passed to the next then block, making a meaningful promise chain. Thanks for #Matt's follow-up feedback below regarding this point.
Two points:
Drop .catch(function(err) { throw new Error() }. It does nothing but remove the error message.
You can unnest the inner then calls
So it just should be
User.find({where:{username:'user'}})
.then(function(user) {
return User.find({where:{username:'other_user'}})
})
.then(function(other_user) {
// do stuff
return whatever_i_need
})
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
})
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
})
I have the below function which calls a promise object
export const project_list = function(data,res){
return db.any(queries.ProjectList)
.then(function(results){
let newResults = project_list_cleaner(results)
res(null,newResults)
})
.catch(function(err){
res(err)
})
}
I'm trying to test the function like the below
it('should retrieve a list',function(){
return expect(project_list(data,res)).to.eventually.be.false
})
this raised an error because the promise object doesnt actually return anything. it exectes the res callback.
is there anyway to test whether the promise object executes the callback?
The fact that the function above returns a Promise does not matter for it's signature. The fact that you are passing a callback to it makes the Promise thing not usable.
Solution 1: refactor your function
Make your function return a Promise and that's it. No callback involved.
export const project_list = function(data){
return db.any(queries.ProjectList)
.then(function(results){
return project_list_cleaner(results)
});
}
The test will then be:
it('should retrieve a list',function(){
return expect(project_list(data)).to.eventually.be.false
})
Solution 2: ignore the Promise
In this solution you don't refactor the function (but you really should!) and work as Promises never existed.
it('should retrieve a list',function(done){
project_list(data, function(err, result){
// put your assertions here
done();
});
})
I have the following requirement,I have three asynchronous function async1(),async2(),ascync3() which all return promises
Now I would call one function and serially execute async1,async2,async3 respectively and I want to print the resolved promise returned after async3
this is my main function
testPromiseSerially = function() {
return new promise(function(resolve,reject) {
async1().
then(function(result1) {
return async2(result1)
})
.then(function(result2){
return async3(result2)
})
.catch(function(err) {
return reject(err)
}
})
}
This is my async3 function
async3 = function(params) {
return new promise(function(resolve,reject) {
return resolve("solved")
})
}
and async1 and async2 are also similar to async3
If I execute this code
testPromiseSerially.then(function(result) {
console.log(result)
})
.catch(function (err) {
console.log(err)
})
testPromiseSerially is getting called but it's not entering 'then' or 'catch'
block.Is promise returned by async3 not relayed back to testpromiseSerially()?
How do I see the result from async3?
I know that if I extend my code like adding
.then(function(result) {
return resolve(result)
})
after async3(result) then I would be able to see the result. But I have chain of functions which depend on promise returned by other functions, so how do I handle that?
The main problem is that your testPromiseSerially code is never calling resolve. So the promise it returns is never resolved.
Since what it's testing already has promises, there's no need for you to create a new one. Every call to then produces a new promise, so just use that.
Additionally, this:
.then(function(result1) {
return async2(result1);
})
is more complicated/verbose than you need, it can be just:
.then(async2)
And the same for the .catch.
So:
let testPromiseSerially = function() {
return async1()
.then(async2)
.then(async3);
};
Example using JavaScript's native promises on Babel's REPL
You should use async https://github.com/caolan/async. It is be better for your case. Look at the waterfall function.
From the documentation
waterfall(tasks, [callback])
Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.
The scope variable $scope.quizArray appears as undefined when accessed from the controller. The block of code below is definitely read as I have previously put tests in.
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
var $promise=$http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
$promise.then(function(msg){
scope.quizArray = "TEST";
});
}
}
});
Controller code - The service is called by:
function getQuizList(){
getQuizService.getQuiz(videoIdService.getId(),$scope);
alert($scope.quizArray);
}
however the alert produces the result 'undefined' instead of 'TEST'
Can anyone see where I've gone wrong? Any help would be appreciated. Thanks
Is there a reason you're doing it like this?
Take a look at the approach(es) below:
Refer to: http://docs.angularjs.org/api/ng/service/$q for more information about how the promises work.
For example, the reason why the approach that lib3d has pointed out works:
then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.
This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback. It also notifies via the return value of the notifyCallback method. The promise can not be resolved or rejected from the notifyCallback method.
Code:
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
return $http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
//If you want to handle the promise here, you can include the $q service in your controller and do this:
var deferred = $q.defer();
$http.post("...").then(function(data) {
//Special treatment here
deferred.resolve(data)
}, function(error) {
deferred.reject(error)
})
return deferred.promise
//As indicated by lib3d, you can directly do this as well:
return $http.post("...").then(function(data) {
//treatment
return data
}, function(error) {
//error handling
});
}
}).controller("Ctrl", function($scope, getQuizService) {
getQuizService.getQuiz(1).then(function(data) {
$scope.quizArray = data;
//Code when everything goes ok,
}, function(error) {
//Code when everything goes false;
})
})