I need to call three WS services before calling a local function depending on whether some variables are defined or not, but the function is getting called before the services get any response, because it could take some time. I've even tried with $timeout, but it does not work
$scope.$on('search', function (event, data) {
self.searchDto= data;
if (self.searchDto.userCode) {
self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
});
}
if (self.searchDto.companyCode) {
self.serachByCompanyCode(self.searchDto.companyCode).then(function (data) {
self.companyCode= data.find(function (item) {
return item.mstId === self.searchDto.companyCode;
});
});
}
if (self.searchDto.jobCode) {
self.searchByJobCode(self.searchDto.jobCode).then(function (data) {
self.jobCode= data.find(function (item) {
return item.mstId === self.searchDto.jobCode;
});
});
}
//I tried with this timeout but it didnt work
$timeout(function () {
self.searchPeople();
}, 1000);
});
Does anyone have idea how the searchPeople method can be called after the WS responses?
Use promises and $q.all()
var promises = [];
promises.push(self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
}));
.then() returns a promise. Do that for the 3 service calls and then wait for their completion
$q.all(promises).then(function(){
self.searchPeople();
})
I see that you might not call all of your services. $q.all() will wait for the promise you put in the array. Keep in mind it will also execute your call if none of your services has been executed, if you need at least one to be called, you might want to add a check for promises.length > 0 before $q.all().
That way, if you only call one of your services, the promises array will have one element and upon its completion, will call your local function.
Setting timeout is not a correct approaching here. One solution can be: you should put 3 WS nested and put the function call inside the last WS callback.
It also depends on how much arguments that your searchPeople need. If it only work with fully 3 arguments from WS calls, another solution is putting the function call in all 3 WS callback, and inside function searchPeople, you should add a condition statement to check if we have fully 3 argument before do searching
Related
How to add code that will run only when both processes are completed?
normalise1();
normalise2();
function normalise1() {
return knex("ingredients")
.select("name", "id")
.map(function (ingredient) {
var normalised_name = common.normalise(ingredient.name);
knex('ingredients').where('id', ingredient.id).update({ name_normalised: normalised_name }).then();
});
};
function normalise2() {
return knex("synonyms")
.select("synon_name as name", "id")
.map(function (ingredient) {
var normalised_name = common.normalise(ingredient.name);
knex('synonyms').where('id', ingredient.id).update({ synon_name_normalised: normalised_name }).then();
});
};
I tried something like in different ways
Promise.all([normalise1(), normalise2()])
.then(() => console.log('Done'));
but it didn't work.
Basically console.log('Done') appears before all process is done. I believe that this is because of missing Promise part inside functions, but I cannot figure out exactly how.
The functions are not called when passed to Promise.all(), no Promise is returned from .map().
Call the functions and return knex() from .map() call, which may also require using Promise.all() within the function calls.
I'm using the popular node library, got, to make simple GET requests to a JSON API.
I have a function that abstracts the request, like so:
function performRequest(url) {
got(url, {
json: true
}).then(function (response) {
return formatResponse(response.body);
}).catch(function (error) {
console.log(error.response.body);
});
}
formatResponse is a simple synchronous method that modifies the JSON returned from the API.
I would like to be able to call performRequest from another function and then use the return value (once resolved). Currently, as performRequest is not recognized as an async method, my code is calling it and then proceeding immediately.
function myBigFunction() {
var url = composeUrl();
var res = performRequest(url);
doMoreStuffWithResponse(res);
}
I know that I need to utilize a Promise, however, I'm always unclear as to how to use a Promise in conjunction with a built-in library function that is already using a Promise (like in this case).
I'm also completely open to the possibility that I'm going about this all wrong. In that case, I would appreciate some redirection.
Thank you for your time.
Understand what a Promise is. Its a value, you can treat it as such. In order to "read" the value, you pass a function to the Promise's then method. You don't need myBigFunction. Anything you want to run after the Promise resolves just needs to be passed to then:
var req = performRequest(composeURL());
req.then(doStuffWithResponse);
Now, I don't particularly care for this way although I do it fairly often. I prefer to have functions that take promises and invoke their then method:
var takesAPromise = function(p) {
return p.then(/* does stuff */);
};
Note that it returns the Promise of the completed task. But what I like even better is this ES6 one-liner:
let wrap = f => p => p.then(val => f.call(null, val));
Now you can wrap arbitrary functions to take Promises as input and return them as output. If Promises were a monad, this would be their bind function. Making it work seamlessly with functions of arbitrary arity is left as an exercise to the reader.
You'll always want to return a promise from your functions:
function performRequest(url) {
return got(url, {
//^^^^^^
json: true
}).then(function(response) {
return formatResponse(response.body);
}, function(error) {
throw new Error(error.response.body);
});
}
With this, you can wait for the result in your big functions using another then:
function myBigFunction() {
var url = composeUrl();
var promise = performRequest(url);
return promise.then(function(res) {
return doMoreStuffWithResponse(res);
});
}
or in short
function myBigFunction() {
return performRequest(composeUrl()).then(doMoreStuffWithResponse);
}
so that you can call it like
myBigFunction().catch(function(error) {
console.log(error.message);
});
I'm new on AngularJS and JavaScript.
I am getting remote information for each of the elements of an array (cars) and creating a new array (interested prospects). So I need to sync the requests. I need the responses of each request to be added in the new array in the same order of the cars.
I did it first in with a for:
for (a in cars) {
//async request
.then(function () {
//update the new array
});
}
This make all the requests but naturally didn't update the new array.
After seeking in forums, I found this great examples and explanations for returning a intermediate promise and sync all of them.
1. http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
2. http://stackoverflow.com/questions/25605215/return-a-promise-from-inside-a-for-loop
3. http://www.html5rocks.com/en/tutorials/es6/promises/
(#MaurizioIndenmark, #Mark Rajcok , #Michelle Tilley, #Nolan Lawson)
I couldn't use the Promise.resolve() suggested in the second reference. So I had used $q.defer() and resolve(). I guess I have to inject a dependency or something else that I missed. As shown below:
In the Controller I have:
$scope.interestedProspects = [] ;
RequestDetailsOfAsync = function ($scope) {
var deferred = $q.defer();
var id = carLists.map(function (car) {
return car.id;
}).reduce(function (previousValue, currentValue) {
return previousValue.then(function () {
TheService.AsyncRequest(currentValue).then(function (rData) {
$scope.interestedProspects.push(rData);
});
});
}, deferred.resolve());
};
In the Service I have something like:
angular.module('app', []).factory('TheService', function ($http) {
return {
AsyncRequest = function (keyID) {
var deferred = $q.defer();
var promise = authorized.get("somep.provider.api/theService.json?" + keyID).done(function (data) {
deferred.resolve(data);
}).fail(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
}
}
The displayed error I got: Uncaught TypeError: previousValue.then is not a function
I made a jsfiddle reusing others available, so that it could be easier to solve this http://jsfiddle.net/alisatest/pf31g36y/3/. How to wait for AsyncRequests for each element from an array using reduce and promises
I don't know if the mistakes are:
the place where the resolve is placed in the controller function.
the way the reduce function is used
The previousValue and currentValue sometimes are seen by javascript like type of Promise initially and then as a number. In the jsfiddle I have a working example of the use of the reduce and an http request for the example.
Look at this pattern for what you want to do:
cars.reduce(function(promise, car) {
return promise.then(function(){
return TheService.AsyncRequest(car).then(function (rData) {
$scope.details.push(rData);
});
});
}, $q.when());
This will do all the asynchronous calls for every car exactly in the sequence they are in the cars array. $q.all may also be sufficient if the order the async calls are made doesn't matter.
It seems you are calling reduce on an array of ids, but assume in the passed function that you are dealing with promises.
In general, when you want to sync a set of promises, you can use $q.all
You pass an array of promises and get another promise in return that will be resolved with an array of results.
I'm having trouble wrapping my head around promises. I'm using the Google Earth API to do a 'tour' of addresses. A tour is just an animation that lasts about a minute, and when one completes, the next should start.
Here's my function that does a tour:
var tourAddress = function (address) {
return tourService.getLatLong(address).then(function (coords) {
return tourService.getKmlForCoords(coords).then(function (kml) {
_ge.getTourPlayer().setTour(kml);
_ge.getTourPlayer().play();
var counter = 0;
var d = $q.defer();
var waitForTour = function () {
if (counter < _ge.getTourPlayer().getDuration()) {
++counter;
setTimeout(waitForTour, 1000);
} else {
d.resolve();
}
};
waitForTour();
return d.promise;
});
});
}
This seems to work pretty well. It starts the animation and returns a promise that resolves when the animation is complete. Now I have an array of addresses, and I want to do a tour foreach of them:
$scope.addresses.forEach(function (item) {
tourAddress(item.address).then(function(){
$log.log(item.address + " complete");
});
});
When i do this, they all execute at the same time (Google Earth does the animation for the last address) and they all complete at the same time. How do I chain these to fire after the previous one completes?
UPDATE
I used #phtrivier's great help to achieve it:
$scope.addresses.reduce(function (curr,next) {
return curr.then(function(){
return tourAddress(next.address)
});
}, Promise.resolve()).then(function(){
$log.log('all complete');
});
You're right, the requests are done immediately, because calling tourAddress(item.address) does a request and returns a Promise resolved when the request is done.
Since you're calling tourAddress in a loop, many Promises are generated, but they don't depend on each other.
What you want is to call tourAdress, take the returned Promise, wait for it to be resolved, and then call tourAddress again with another address, and so on.
Manually, you would have to write something like :
tourAddress(addresses[0])
.then(function () {
return tourAddress(addresses[1]);
})
.then(function () {
return tourAddress(addresses[2]);
})
... etc...
If you want to do that automatically (you're not the first one : How can I execute array of promises in sequential order?), you could try reducing the list of address to a single Promise that will do the whole chain.
_.reduce(addresses, function (memo, address) {
return memo.then(function (address) {
// This returns a Promise
return tourAddress(address);
});
}, Promise.resolve());
(This is pseudo-code that uses underscore, and bluebird, but it should be adaptable)
It's because you're doing things asynchronously so they'll all run at the same time. This answer should help you rewrite your loop so each address is run after the next one.
Asynchronous Loop of jQuery Deferreds (promises)
I have a function which will generate the WinJS.xhr promise and return the same to the calling function. But after getting the promise, when doing a .then on it, all I'm getting is an empty array!!!
APPROACH 1:
Here is the function which is returning a promise. It's inside a WinJS.Class.define :
getFeaturedData: function () {
var featuredUrl = utils.getRequestUrl(globals.featuredTag, 1, 0);
return WinJS.xhr({ url: featuredUrl });
},
I'm calling that function in home.js and attaching a .then this way:
var promise = MyApp.Services.Movies.getFeaturedData();
promise.then(function(success) {
var data = success;
},
function (error) {
})
The result variable data is always an empty array which I can't seem to understand why.
APPROACH 2:
If I do .then in the getFeaturedData function itself then it works, surprisingly.
getFeaturedData: function () {
var featuredUrl = utils.getRequestUrl(globals.featuredTag, 1, 0);
var promise = WinJS.xhr({ url: featuredUrl });
promise.then(function (success) {
var data = success;
})
},
In this case, data seems to contain proper data returned from the server.
Can anyone explain this behavior? Why the first approach doesn't work and the second one does?
In example 1, getFeaturedData is returning a promise.
In example 2, it returns nothing (i.e. undefined).
However, the timing when "data" is set is unchanged. You're setting the "data" value in the completion handler of the XHR in both cases. Most likely the difference lies in where you had set your breakpoint. In neither case will "data" be set at the end of getFeaturedData, or at the end of the calling block of code. Instead, it will be set sometime later when the XHR promise completes.
A couple other notes:
Approach 2 can return the result of promise.then() (which is another promise) so that the caller can actually schedule work to happen once data is set.
In both cases, the "promise" variable is kind of extraneous, or it could be set to the result of either getFeaturedData().then(), or xhr().then().
I don't if it is a typo but your first approach is missing an ')' at the end of the function error to enclose the promise result.
Other than that, I can't see why the 1st approach wouldn't work.
Having said that, here's how I would write it:
getFeaturedData: function () {
return new WinJS.Promise(function (complete, fail){
var featuredUrl = utils.getRequestUrl(globals.featuredTag, 1, 0);
WinJS.xhr({ url: featuredUrl }).then(
function (data){
complete(data);
}, function (err){
fail(err);
});
});
}
MyApp.Services.Movies.getFeaturedData().then(
function (data){
//do whatever you want with the data
}, function (err){
// handle errors here
});
It is easier for me to see that getFeaturedData returns a promise. In your case it is clear since it is well known that WinJS.xhr returns a promise, but it will not always be that easy to 'see' right away.