How to wait for _.each loop with ajax call to complete? - javascript

Take the following example:
_.each(arrayOfVals, function (val) {
$.when(getAjaxCall('foo',{val:val}))
.then(function (callResponse) {
_.each(callResponse, function (rep) {
console.log(rep);
});
});
I then want to call some code after the entirety of that code is complete. How can I do this?

You can pass multiple arguments to $.when and its deferred will resolve when they all complete. If you have an array, use .apply.
$.when.apply($, arrayOfVals.map(val => getAjaxCall('foo', {val}))
.then(responses => responses.map(resp => console.log(resp));

I ended up using a $.Deferred object to listen to when everything is done and called the resolve when I've gone through the entire list.
deferred = $.Deferred
i = 0;
_.each(arrayOfVals, function (val) {
$.when(getAjaxCall('foo',{val:val}))
.then(function (callResponse) {
_.each(callResponse, function (rep) {
i += 1;
console.log(rep);
if (i == callResponse.length) {
deferred.resolve();
}
});
});
deferred.done(function() {
console.log('everything done!');
}

Related

How to wait until jQuery function has finished before proceeding?

async function tempfunc2(result) {
return new Promise((resolve, reject) => {
for(var i = 0; i < result.length; i++)
{
var url = "SomeURLGeneratedByPreviousFunction";
$.getJSON(url, function(data) {
if (data.mappings["0"]) {
gameHashes.push(data.mappings["0"].game);
}
});
}
return resolve(gameHashes);
});
}
I have this code block that is getting and populating data. Its handling quite a bit of data, so it takes some time.
What I want is for this function to complete before finishing because the next lines of code rely on the result of this function.
However, the way its currently built, it will return the Hashes before the function is complete. How do I await this jQuery function?
I tried putting a .then() after the $.getJSON, but it didn't change much.
I also tried putting this particular piece in a different function to try and await it, but that did not work either
await tempfunc().then(tempfunc2);
This is what calls tempfunc2.
You need to resolve the promise once you get the response from $.getJSON.
Something like this:
async function tempfunc2(result) {
return new Promise((resolve, reject) => {
$.getJSON(url, function(data) {
if (data.mappings["0"]) {
gameHashes.push(data.mappings["0"].game);
}
resolve(gameHashes)
});
})
}
Make sure gameHashes is defined as an array.
From the edits to your question it seems that you're making multiple &.getJSON calls. In that case, you need to do something like this:
function tempfunc2(result) {
return new Promise(async resolve => {
const promises = result.map(value => {
return new Promise(resolve => {
$.getJSON(url, function(data) {
resolve(data)
});
});
})
let results = await Promise.all(promises)
results = results.filter(v => v.mapping && v.mapping["0"]).map(v => v.mapping["0"].game)
resolve(results);
});
}
new Promise is known as promise construction antipattern in cases when a promise already exists.
$.getJSON returns jQuery deferred object, it can produce a promise that can be awaited:
async function tempfunc2(result) {
const data = await $.getJSON(url).promise();
if (data.mappings["0"])
gameHashes.push(data.mappings["0"].game);
return gameHashes;
}

Table walk / recursion with Promise

I would like to walk a database table using Promises to get the data from each step synchronously. I think my code should look something like:
function get_next_id(the_id) {
return new Promise(function(resolve) {
connection.query(get_parent_query, [ the_id ], function (e, r, f) {
resolve(r[0].from_visit);
});
});
}
var page_id = 60239;
while (page_id > 0) {
get_next_id(page_id).then((i) => page_id = i);
}
The problem with this code is that the loop iterates immediately without waiting for the then() to complete.
In this answer the poster suggests either using Promise.race() or abandoning Promise altogether in favor of async.
May use async / await:
(async function(){
var pageId = 60239;
while (page_id > 0) {
pageId = await get_next_id(pageId);
}
})()
or use indirect recursion:
(function next(pageId){
if(pageId <= 0) return;
get_next_id(pageId).then(next);
})(60239);
I don't understand why you want to get a bunch of id's but not do anything with the results. Your original function was almost there but you should reject with the error and the results so far if something goes wrong.
And resolve with all the results if everything goes right:
function get_next_id(the_id,results=[]) {
return new Promise(function (resolve,reject) {
connection.query(get_parent_query, [the_id], function (e, r, f) {
if(e){
//reject if something goes wrong with error and
// what has been done so far
reject([e,results]);
return;
}
resolve(r);
});
})
.then(function (r){
if(r[0].from_visit===0){
return results;
}
//recusively call unless id is 0
return get_next_id(r[0].from_visit,results.concat(r))
});
}
get_next_id(22)
.then(
results=>console.log("got results:",results)
,([error,resultsSoFar])=>console.error(
"something went wrong:",error,
"results before the error:",resultsSoFar
)
);

Using results of a callback in node in a separate function

I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).

Why isn't .then() waiting for the promise?

I am currently working on a angular project, and I am kind of new to it.
I do not understand, why is .then() function not waiting for the promises?
I think it have to do something with that I only have one $q.defer() inside my getAllStats() function? When I try to console.log("testing: ", data); (on the bottom) it only logs out an empty array. Could someone help me please?
This is my code:
function getAllStats(dataArray, nameOfFile) {
var defer = $q.defer();
var promises = [];
for (index in dataArray) {
if (dataArray[index].indexOf('test') > -1 ) {
getStats(nameOfFile).then(function (data) {
promises.push();
});
}
}
function last() {
defer.resolve(promises);
}
$q.all(promises).then(last);
return defer.promise;
};
function getStats(nameOfFile) {
var defer = $q.defer();
$http.get(nameOfFile).success(function (data) {
defer.resolve(data);
});
return defer.promise;
};
getAllStats('test.txt').then(function(data) {
console.log("testing: ", data);
});
See the comments in this code:
function getAllStats(dataArray, nameOfFile) {
var promises = [];
// using `for (index in dataArray) {` is a bad idea unless
// dataArray is a non-array object
for (var index = 0; index < dataArray.length; index++) {
if (dataArray[index].indexOf('test') > -1 ) {
// Here is the trick, store the promise itself,
// don't try to subscribe to it here
promises.push(getStats(nameOfFile));
}
}
return $q.all(promises);
};
function getStats(nameOfFile) {
// http.get already returns a promise, see explicit promise creation antipattern
return $http.get(nameOfFile).then(function(r) { return r.data; });
};
getAllStats('test.txt').then(function(data) {
console.log("testing: ", data);
});
References:
Explicit promise creation antipattern
Why is for..in bad
Deprecation Notice
The $http legacy promise methods success and error
have been deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
see: $http
In your example this block:
for (index in dataArray) {
if (dataArray[index].indexOf('test') > -1 ) {
getStats(nameOfFile).then(function (data) {
promises.push();
});
}
}
Takes dataArray, which is a string and runs through it char by char. Also, you are not setting nameOfFile. Change the call to:
getAllStats(['test.txt']).then(function(data) {
console.log("testing: ", data);
});
And then to make the push to promises be correct do like this:
promises.push(getStats(dataArray[index]));
Multiple issues wrong here:
You're passing an empty promises array to $q.all(). It has to be an array of promises at the time you pass it to $q.all().
You're creating promises when you can just return the ones you have
getAllStats() expects an array, but you're passing a string.
I'd suggest this overall cleanup of the code that fixes the above issues:
function getAllStats(dataArray) {
var promises = dataArray.filter(function(item) {
return item.indexOf('test') !== -1;
}).map(function(item) {
return $http.get(item);
});
return $q.all(promises);
};
getAllStats(['test.txt']).then(function(data) {
console.log("testing: ", data);
});
I'd also suggest you read about promise anti-patterns to teach yourself how to use the promises you already have and avoid creating new ones when new ones are not necessary.
P.S. I'm not sure what was the point of the nameOfFile argument since you don't want to be getStats() on the same file over and over again.

Multiple jQuery promises in sequential order

Basically I want this:
function do_ajax_calls(...){
var d = $.Deferred();
$.ajax(args).done(function(){
$.ajax(args).done(function(){
$.ajax(args).done(function(){
d.resolve();
});
});
})
return d.promise();
}
But the number of ajax calls depends on the arguments that I pass to the function, which is an array, so I can't use that code.
The function should return a promise that only resolves when the last ajax calls completes. So the function needs to be called like this:
do_ajax_calls(....).done(function(){
// here is the callback
})
Does anyone know how can I do this?
But the number of ajax calls depends on the arguments that I pass to the function, which is an array
If it's one ajax call per array item
function do_ajax_calls(args) {
return args.reduce(function(promise, item) {
return promise.then(function() {
return $.ajax(args); // should that be item?
});
}, Promise.resolve(true));
}
The Promise.resolve(true) is a "native" promise, i.e. not available in IE, but I'm sure jQuery has an equivalent
Here's a JSFiddle Demo
One of the reasons promises are a big deal is because they can be chained. You can use this to your advantage to iteratively chain additional requests onto the resolution of the previous one:
function do_ajax_calls() {
var dfd = $.Deferred();
var promise = dfd.promise();
var responses = [];
function chainRequest(url) {
promise = promise.then(function (response) {
responses.push(response);
return $.ajax(url, { method: 'POST' });
});
}
for (var i = 0, length = arguments.length; i < length; i++) {
chainRequest(arguments[i]);
}
dfd.resolve();
return promise.then(function (response) {
return responses.slice(1).concat(response);
});
}
The above code will return a promise ultimately resolving to an array of all of the responses. If any one of the requests fails, the promise will reject with the first failure.
JSFiddle
Here is it Demo
var counter = 1 ;
function multipleAjax(loop)
{
if(counter<loop)
{
$.ajax(
{
url: 'http://mouadhhsoumi.tk/echo.php',
success:function(data)
{
multipleAjax(loop);
$(".yo").append(data+"</br>");
counter++;
}
});
}
}
multipleAjax(5);
Try using $.when() , Function.prototype.apply() , $.map()
function do_ajax_calls(args) {
return $.when.apply($, $.map(args, function(request, i) {
return $.ajax(request) // `request` : item with `args` array
}))
}
do_ajax_calls
.then(function success() {
console.log(arguments)
}, function err() {
console.log("err", arguments)
});

Categories