This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
What I want to do:
var found = false;
while (!found){
var result = db.getNextRecord();
if (result == search_term){
return result;
}
}
The problem is, getNextRecord is asynchronous
var nothing_returned = db.getNextRecord(function(err, result){
// I have the result in this callback, but not before
});
Given the behavior of getNextRecord(cb), how can I rewrite the above code snippet to get the same outcome?
Since you have a function that's async and you want to call in synchronously, you have two choice. Use a sync version of the method if there is one available, but if not, then you'll have to change your logic.
The following snippet should do what you want, it does require the async library.
var async = require('async');
var result;
async.whilst(
function () {
return !result;
},
function (callback) {
db.getNextRecord(function (err, record) {
if (err)
{
return callback(err);
}
if (result == search_term)
{
result = record;
}
return callback();
});
},
function (err) {
// Search is complete, do what you wish with result in this function. This function
// will be called when whilst is done or if getNextRecord got an error.
}
);
I'm sure there's a shorter way to do this if you want to change the logic even more, but this is similar to doing a while but asynchronously.
Use the async library. Its until function looks like what you need: https://www.npmjs.com/package/async#until
var async = require('async');
var latestResult = null;
async.until(function () {
return latestResult == search_term;
}, function () {
db.getNextRecord(function (err, result) {
latestResult = result;
});
}, function () {
// now you can do something with latestResult
});
You should also consider whether it makes sense to do this in your app or have the database query include this filtering.
With babel and new JS:
import {promisify as pr} from 'es6-promisify';
async function find(search_term) {
let found = false, result=null;
while (!found){
let result = await pr(db.getNextRecord)();
if (result == search_term){
found=true;
}
}
return result;
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I have a function which is passed a list of URLs (in this case a list of json files) that makes asynchronous ajax calls for each resource, and once they are done returns a single object with the contents.
function loadModels(modelList){
var modelObject = {}
for(model in modelList){
var urlPath = modelList[model].indexOf(".json") > -1 ? 'model/'+modelList[model]+'json' : 'model/'+modelList[model]+'.json';
$.ajax(urlPath).done(function(jsonData){
modelObject[modelList[model]] = jsonData;
});
}
console.log(modelObject);
return modelObject;
}
Trouble is, the console always logs Object{} since the function returns before the deferred objects resolve. If I set async: false then it works just fine, but I don't want the requests to wait for each other.
How can I force the function to wait before returning and still make calls asynchronously?
EDIT: The deferred solution in the link isn't a complete enough answer. The issue I was running into was related to closures. Here's the working code block:
function loadModels(modelList, callback){
var promises = [];
for(model in modelList){
(function(){
var currentModel = modelList[model];
var urlPath = currentModel.indexOf(".json") > -1 ? 'model/'+currentModel+'json' : 'model/'+currentModel+'.json';
var promise = $.ajax(urlPath);
log('pulling model: ' + currentModel);
promise.done(function(jsonData){
modelObject[currentModel] = jsonData;
log('model pulled: ' + currentModel);
});
promises.push(promise);
})();
}
$.when.apply(null, promises).done(function() {
callback(modelObject);
});
}
You can use jQuery.when() to wait for all the promises to resolve before continuing...
var promises = [];
for(model in modelList){
var urlPath = modelList[model].indexOf(".json") > -1 ? 'model/'+modelList[model]+'json' : 'model/'+modelList[model]+'.json';
var promise = $.ajax(urlPath);
promises.push(promise);
promise.done(function(jsonData){
modelObject[modelList[model]] = jsonData;
});
}
$.when.apply(null, promises).done(function() {
console.log(modelObject);
});
when the last ajax-done event is finished trigger an event and pass your object to the event data
and where you need the data handle that event.
$.event.trigger({
type: "afterMyAjaxDone",
data: MyDataObject
});
$(document).on('afterMyAjaxDone', function (e) {
var MyDataObject = e.data;
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I'm using jQuery. I have a function that fetches data from a remote server called "get_cats". I call it to fill an array with the values returned. When AJAX is completed I want to return the values.
But It doesn't work, the value returned is undefined. This is pretty basic but I can't see where It fails. Does anyone has a clue ?
$(function () {
var url = "http://someurl.com/service/";
var cats = [];
cats = get_cats(url);
function get_cats(url) {
var categories = [];
$.getJSON(url + "cats", function (data) {
$.each(data, function (i) {
categories.push(data[i].name);
});
return categories;
});
}
$(document).ajaxStop(function () {
console.log(cats); // fails and returns undefined :'(
});
});
Oh no AJAX is asynchronous, you cannot return anything from it. You should consume the results of an AJAX request only inside the success callback:
$(function () {
var url = "http://someurl.com/service/";
get_cats(url);
function get_cats(url) {
var categories = [];
$.getJSON(url + "cats", function (data) {
$.each(data, function (i) {
categories.push(data[i].name);
});
// Only here you can consume the results of the AJAX request
// Do not attempt to return them, it doesn't make any sense
console.log(categories);
});
}
});
You can try:
$.ajaxSetup({async:false});
before AJAX call.
But it will stop browser while respone will be returned.
I'd like to update a page based upon the results of multiple ajax/json requests. Using jQuery, I can "chain" the callbacks, like this very simple stripped down example:
$.getJSON("/values/1", function(data) {
// data = {value: 1}
var value_1 = data.value;
$.getJSON("/values/2", function(data) {
// data = {value: 42}
var value_2 = data.value;
var sum = value_1 + value_2;
$('#mynode').html(sum);
});
});
However, this results in the requests being made serially. I'd much rather a way to make the requests in parallel, and perform the page update after all are complete. Is there any way to do this?
jQuery $.when() and $.done() are exactly what you need:
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);
Try this solution, which can support any specific number of parallel queries:
var done = 4; // number of total requests
var sum = 0;
/* Normal loops don't create a new scope */
$([1,2,3,4,5]).each(function() {
var number = this;
$.getJSON("/values/" + number, function(data) {
sum += data.value;
done -= 1;
if(done == 0) $("#mynode").html(sum);
});
});
Run multiple AJAX requests in parallel
When working with APIs, you sometimes need to issue multiple AJAX requests to different endpoints. Instead of waiting for one request to complete before issuing the next, you can speed things up with jQuery by requesting the data in parallel, by using jQuery's $.when() function:
JS
$.when($.get('1.json'), $.get('2.json')).then(function(r1, r2){
console.log(r1[0].message + " " + r2[0].message);
});
The callback function is executed when both of these GET requests finish successfully. $.when() takes the promises returned by two $.get() calls, and constructs a new promise object. The r1 and r2 arguments of the callback are arrays, whose first elements contain the server responses.
Here's my attempt at directly addressing your question
Basically, you just build up and AJAX call stack, execute them all, and a provided function is called upon completion of all the events - the provided argument being an array of the results from all the supplied ajax requests.
Clearly this is early code - you could get more elaborate with this in terms of the flexibility.
<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
var ParallelAjaxExecuter = function( onComplete )
{
this.requests = [];
this.results = [];
this.onComplete = onComplete;
}
ParallelAjaxExecuter.prototype.addRequest = function( method, url, data, format )
{
this.requests.push( {
"method" : method
, "url" : url
, "data" : data
, "format" : format
, "completed" : false
} )
}
ParallelAjaxExecuter.prototype.dispatchAll = function()
{
var self = this;
$.each( self.requests, function( i, request )
{
request.method( request.url, request.data, function( r )
{
return function( data )
{
console.log
r.completed = true;
self.results.push( data );
self.checkAndComplete();
}
}( request ) )
} )
}
ParallelAjaxExecuter.prototype.allRequestsCompleted = function()
{
var i = 0;
while ( request = this.requests[i++] )
{
if ( request.completed === false )
{
return false;
}
}
return true;
},
ParallelAjaxExecuter.prototype.checkAndComplete = function()
{
if ( this.allRequestsCompleted() )
{
this.onComplete( this.results );
}
}
var pe = new ParallelAjaxExecuter( function( results )
{
alert( eval( results.join( '+' ) ) );
} );
pe.addRequest( $.get, 'test.php', {n:1}, 'text' );
pe.addRequest( $.get, 'test.php', {n:2}, 'text' );
pe.addRequest( $.get, 'test.php', {n:3}, 'text' );
pe.addRequest( $.get, 'test.php', {n:4}, 'text' );
pe.dispatchAll();
</script>
here's test.php
<?php
echo pow( $_GET['n'], 2 );
?>
Update: Per the answer given by Yair Leviel, this answer is obsolete. Use a promise library, like jQuery.when() or Q.js.
I created a general purpose solution as a jQuery extension. Could use some fine tuning to make it more general, but it suited my needs. The advantage of this technique over the others in this posting as of the time of this writing was that any type of asynchronous processing with a callback can be used.
Note: I'd use Rx extensions for JavaScript instead of this if I thought my client would be okay with taking a dependency on yet-another-third-party-library :)
// jQuery extension for running multiple async methods in parallel
// and getting a callback with all results when all of them have completed.
//
// Each worker is a function that takes a callback as its only argument, and
// fires up an async process that calls this callback with its result.
//
// Example:
// $.parallel(
// function (callback) { $.get("form.htm", {}, callback, "html"); },
// function (callback) { $.post("data.aspx", {}, callback, "json"); },
// function (formHtml, dataJson) {
// // Handle success; each argument to this function is
// // the result of correlating ajax call above.
// }
// );
(function ($) {
$.parallel = function (anyNumberOfWorkers, allDoneCallback) {
var workers = [];
var workersCompleteCallback = null;
// To support any number of workers, use "arguments" variable to
// access function arguments rather than the names above.
var lastArgIndex = arguments.length - 1;
$.each(arguments, function (index) {
if (index == lastArgIndex) {
workersCompleteCallback = this;
} else {
workers.push({ fn: this, done: false, result: null });
}
});
// Short circuit this edge case
if (workers.length == 0) {
workersCompleteCallback();
return;
}
// Fire off each worker process, asking it to report back to onWorkerDone.
$.each(workers, function (workerIndex) {
var worker = this;
var callback = function () { onWorkerDone(worker, arguments); };
worker.fn(callback);
});
// Store results and update status as each item completes.
// The [0] on workerResultS below assumes the client only needs the first parameter
// passed into the return callback. This simplifies the handling in allDoneCallback,
// but may need to be removed if you need access to all parameters of the result.
// For example, $.post calls back with success(data, textStatus, XMLHttpRequest). If
// you need textStatus or XMLHttpRequest then pull off the [0] below.
function onWorkerDone(worker, workerResult) {
worker.done = true;
worker.result = workerResult[0]; // this is the [0] ref'd above.
var allResults = [];
for (var i = 0; i < workers.length; i++) {
if (!workers[i].done) return;
else allResults.push(workers[i].result);
}
workersCompleteCallback.apply(this, allResults);
}
};
})(jQuery);
UPDATE And another two years later, this looks insane because the accepted answer has changed to something much better! (Though still not as good as Yair Leviel's answer using jQuery's when)
18 months later, I just hit something similar. I have a refresh button, and I want the old content to fadeOut and then the new content to fadeIn. But I also need to get the new content. The fadeOut and the get are asynchronous, but it would be a waste of time to run them serially.
What I do is really the same as the accepted answer, except in the form of a reusable function. Its primary virtue is that it is much shorter than the other suggestions here.
var parallel = function(actions, finished) {
finishedCount = 0;
var results = [];
$.each(actions, function(i, action) {
action(function(result) {
results[i] = result;
finishedCount++;
if (finishedCount == actions.length) {
finished(results);
}
});
});
};
You pass it an array of functions to run in parallel. Each function should accept another function to which it passes its result (if any). parallel will supply that function.
You also pass it a function to be called when all the operations have completed. This will receive an array with all the results in. So my example was:
refreshButton.click(function() {
parallel([
function(f) {
contentDiv.fadeOut(f);
},
function(f) {
portlet.content(f);
},
],
function(results) {
contentDiv.children().remove();
contentDiv.append(results[1]);
contentDiv.fadeIn();
});
});
So when my refresh button is clicked, I launch jQuery's fadeOut effect and also my own portlet.content function (which does an async get, builds a new bit of content and passes it on), and then when both are complete I remove the old content, append the result of the second function (which is in results[1]) and fadeIn the new content.
As fadeOut doesn't pass anything to its completion function, results[0] presumably contains undefined, so I ignore it. But if you had three operations with useful results, they would each slot into the results array, in the same order you passed the functions.
you could do something like this
var allData = []
$.getJSON("/values/1", function(data) {
allData.push(data);
if(data.length == 2){
processData(allData) // where process data processes all the data
}
});
$.getJSON("/values/2", function(data) {
allData.push(data);
if(data.length == 2){
processData(allData) // where process data processes all the data
}
});
var processData = function(data){
var sum = data[0] + data[1]
$('#mynode').html(sum);
}
Here's an implementation using mbostock/queue:
queue()
.defer(function(callback) {
$.post('/echo/json/', {json: JSON.stringify({value: 1}), delay: 1}, function(data) {
callback(null, data.value);
});
})
.defer(function(callback) {
$.post('/echo/json/', {json: JSON.stringify({value: 3}), delay: 2}, function(data) {
callback(null, data.value);
});
})
.awaitAll(function(err, results) {
var result = results.reduce(function(acc, value) {
return acc + value;
}, 0);
console.log(result);
});
The associated fiddle: http://jsfiddle.net/MdbW2/
With the following extension of JQuery (to can be written as a standalone function you can do this:
$.whenAll({
val1: $.getJSON('/values/1'),
val2: $.getJSON('/values/2')
})
.done(function (results) {
var sum = results.val1.value + results.val2.value;
$('#mynode').html(sum);
});
The JQuery (1.x) extension whenAll():
$.whenAll = function (deferreds) {
function isPromise(fn) {
return fn && typeof fn.then === 'function' &&
String($.Deferred().then) === String(fn.then);
}
var d = $.Deferred(),
keys = Object.keys(deferreds),
args = keys.map(function (k) {
return $.Deferred(function (d) {
var fn = deferreds[k];
(isPromise(fn) ? fn : $.Deferred(fn))
.done(d.resolve)
.fail(function (err) { d.reject(err, k); })
;
});
});
$.when.apply(this, args)
.done(function () {
var resObj = {},
resArgs = Array.prototype.slice.call(arguments);
resArgs.forEach(function (v, i) { resObj[keys[i]] = v; });
d.resolve(resObj);
})
.fail(d.reject);
return d;
};
See jsbin example:
http://jsbin.com/nuxuciwabu/edit?js,console
The most professional solution for me would be by using async.js and Array.reduce like so:
async.map([1, 2, 3, 4, 5], function (number, callback) {
$.getJSON("/values/" + number, function (data) {
callback(null, data.value);
});
}, function (err, results) {
$("#mynode").html(results.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}));
});
If the result of one request depends on the other, you can't make them parallel.
Building on Yair's answer.
You can define the ajax promises dynamically.
var start = 1; // starting value
var len = 2; // no. of requests
var promises = (new Array(len)).fill().map(function() {
return $.ajax("/values/" + i++);
});
$.when.apply($, promises)
.then(myFunc, myFailure);
Suppose you have an array of file name.
var templateNameArray=["test.html","test2.html","test3.html"];
htmlTemplatesLoadStateMap={};
var deffereds=[];
for (var i = 0; i < templateNameArray.length; i++)
{
if (!htmlTemplatesLoadStateMap[templateNameArray[i]])
{
deferreds.push($.get("./Content/templates/" +templateNameArray[i],
function (response, status, xhr) {
if (status == "error") { }
else {
$("body").append(response);
}
}));
htmlTemplatesLoadStateMap[templateNameArray[i]] = true;
}
}
$.when.all(deferreds).always(function(resultsArray) { yourfunctionTobeExecuted(yourPayload);
});
I needed multiple, parallel ajax calls, and the jquery $.when syntax wasn't amenable to the full $.ajax format I am used to working with. So I just created a setInterval timer to periodically check when each of the ajax calls had returned. Once they were all returned, I could proceed from there.
I read there may be browser limitations as to how many simultaneous ajax calls you can have going at once (2?), but .$ajax is inherently asynchronous, so making the ajax calls one-by-one would result in parallel execution (within the browser's possible limitation).