Related
I use jQuery. And I don't want parallel AJAX calls on my application, each call must wait the previous before starting. How to implement it? There is any helper?
UPDATE If there is any synchronous version of the XMLHttpRequest or jQuery.post I would like to know. But sequential != synchronous, and I would like an asynchronous and sequential solution.
There's a much better way to do this than using synchronous ajax calls. Jquery ajax returns a deferred so you can just use pipe chaining to make sure that each ajax call finishes before the next runs. Here's a working example with a more in depth example you can play with on jsfiddle.
// How to force async functions to execute sequentially
// by using deferred pipe chaining.
// The master deferred.
var dfd = $.Deferred(), // Master deferred
dfdNext = dfd; // Next deferred in the chain
x = 0, // Loop index
values = [],
// Simulates $.ajax, but with predictable behaviour.
// You only need to understand that higher 'value' param
// will finish earlier.
simulateAjax = function (value) {
var dfdAjax = $.Deferred();
setTimeout(
function () {
dfdAjax.resolve(value);
},
1000 - (value * 100)
);
return dfdAjax.promise();
},
// This would be a user function that makes an ajax request.
// In normal code you'd be using $.ajax instead of simulateAjax.
requestAjax = function (value) {
return simulateAjax(value);
};
// Start the pipe chain. You should be able to do
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();
// Deferred pipe chaining.
// What you want to note here is that an new
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {
values.push(x);
dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Process the response here.
});
});
}
Some people have commented they have no clue what the code does. In order to understand it, you first need to understand javascript promises. I am pretty sure promises are soon to be a native javascript language feature, so that should give you a good incentive to learn.
You have two choices that I can think of. One is to chain them through callbacks. The other is to make the calls synchronous rather than async.
Is there a reason you want them sequential? That will slow things down.
To make the call synchronous, you'll set the async option in the Ajax call to false. See the documentation at http://docs.jquery.com/Ajax/jQuery.ajax#options (click options tab to see them).
(async () => {
for(f of ['1.json','2.json','3.json']){
var json = await $.getJSON(f);
console.log(json)
};
})()
requests 3 json files with jQuery ajax calls
process in sequence (not in parallel) with await
works in Chrome/Firefox/Edge (as of 1/30/2018)
more at MDN
The best way you could do this is by chaining callbacks as Nosredna said. I wouldn't recommend using synchronous XMLHttpRequest as they lock your entire application.
There aren't much helper for this as far as I know, but you could do something resembling a callback FIFO.
You could give narrative javascript a try http://www.neilmix.com/narrativejs/doc/
I've never used it myself though. If I wanted to do this, I would setup some kind of abstraction for chaining asynchronous actions. As others have said, the synchonous version of the ajax object blocks events from being processed while it's waiting for a response. This causes the browser to look like it's frozen until it recieves a response.
Set the async option to false, e.g.,
$.ajax({ async: false /*, your_other_ajax_options_here */ });
Reference: Ajax/jQuery.ajax
You can use promise to make ajax calls sequential. Using Array push and pop promise method, sequential ajax calls will be lot easier.
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(id) {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/'+id,
dataType:'json',
success: function(data)
{
console.log("Ajax Request Id"+id);
console.log(data);
resolve();
}
});
});
}
function pushPromise(id)
{
promises.push(promises.pop().then(function(){
return methodThatReturnsAPromise(id)}));
}
pushPromise(1);
pushPromise(3);
pushPromise(2);
Look at this: http://docs.jquery.com/Ajax/jQuery.ajax (click on the "options" tab).
But remember a synchronous call will freeze the page until the response is received, so it can't be used in a production site, because users will get mad if for any reason they have to wait 30 seconds with their browser frozen.
EDIT: ok, with your update it's clearer what you want to achieve ;)
So, your code may look like this:
$.getJSON("http://example.com/jsoncall", function(data) {
process(data);
$.getJSON("http://example.com/jsoncall2", function (data) {
processAgain(data);
$.getJSON("http://example.com/anotherjsoncall", function(data) {
processAgainAndAgain(data);
});
});
});
This way, the second call will only be issued when the response to the first call has been received and processed, and the third call will only be issued when the response to the second call has been received and processed. This code is for getJSON but it can be adapted to $.ajax.
The modern way of sequencing jQuery asynchronous operations is to use the promises they already return and the flow control that promises support and this is not currently shown in any of the other answers here from prior years.
For example, let's suppose you wanted to load several scripts with $.getScript(), but the scripts must be loaded sequentially so the second one doesn't load/run until the first has finished and so on and you want to know when they are all done. You can directly use the promise that $.getScript() already returns. For simplicity, you can await that promise in a for loop like this:
async function loadScripts(scriptsToLoad) {
for (const src of scriptsToLoad) {
await $.getScript(src);
}
}
loadScripts([url1, url2, url3]).then(() => {
console.log("all done loading scripts");
}).catch(err => {
console.log(err);
});
Since all jQuery Ajax-related asynchronous operations now return promises (and have for many years now), you can extend this concept to any of jQuery's Ajax-related operations.
Also, note that all the other attempts in other answers here to wrap a jQuery operation in a new promise or in a jQuery deferred are obsolete and considered a promise anti-pattern because when the operation itself already returns a promise, you can just use that promise directly without trying to wrap it in your own new promise.
Synchronous calls aren't necessarily slower, if you have an app where AJAX calls open, posts to, then closes a socket, multiple calls to the socket don't make sense as some sockets can only handle a single connection, in which case, queuing data so its only sent when the previous AJAX call has completed means much higher data throughput.
How about using Node.js events?
var EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var $ = require('jquery');
var doSomething = function (responseData) {
var nextRequestData = {};
// do something with responseData
return nextRequestData;
};
// ajax requests
var request1 = $.ajax;
var request2 = $.ajax;
var requests = [request1, request2];
eventEmitter.on('next', function (i, requestData) {
requests[i](requestData).then(
function (responseData) {
console.log(i, 'request completed');
if (i+1 < requests.length) {
var nextRequestData = doSomething(responseData);
eventEmitter.emit('next', i+1, nextRequestData);
}
else {
console.log('completed all requests');
}
},
function () {
console.log(i, 'request failed');
}
);
});
var data = {
//data to send with request 1
};
eventEmitter.emit('next', 0, data);
sequential != synchronous, and I would like an asynchronous and sequential solution
Synchronous execution generally means "using the same clock", while sequential execution means "following in order or sequence".
For your specific use case I think both conditions must be met, as asynchronous execution implies the possibility of a non-sequential result.
I use jQuery. And I don't want parallel AJAX calls on my application, each call must wait the previous before starting. How to implement it? There is any helper?
UPDATE If there is any synchronous version of the XMLHttpRequest or jQuery.post I would like to know. But sequential != synchronous, and I would like an asynchronous and sequential solution.
There's a much better way to do this than using synchronous ajax calls. Jquery ajax returns a deferred so you can just use pipe chaining to make sure that each ajax call finishes before the next runs. Here's a working example with a more in depth example you can play with on jsfiddle.
// How to force async functions to execute sequentially
// by using deferred pipe chaining.
// The master deferred.
var dfd = $.Deferred(), // Master deferred
dfdNext = dfd; // Next deferred in the chain
x = 0, // Loop index
values = [],
// Simulates $.ajax, but with predictable behaviour.
// You only need to understand that higher 'value' param
// will finish earlier.
simulateAjax = function (value) {
var dfdAjax = $.Deferred();
setTimeout(
function () {
dfdAjax.resolve(value);
},
1000 - (value * 100)
);
return dfdAjax.promise();
},
// This would be a user function that makes an ajax request.
// In normal code you'd be using $.ajax instead of simulateAjax.
requestAjax = function (value) {
return simulateAjax(value);
};
// Start the pipe chain. You should be able to do
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();
// Deferred pipe chaining.
// What you want to note here is that an new
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {
values.push(x);
dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Process the response here.
});
});
}
Some people have commented they have no clue what the code does. In order to understand it, you first need to understand javascript promises. I am pretty sure promises are soon to be a native javascript language feature, so that should give you a good incentive to learn.
You have two choices that I can think of. One is to chain them through callbacks. The other is to make the calls synchronous rather than async.
Is there a reason you want them sequential? That will slow things down.
To make the call synchronous, you'll set the async option in the Ajax call to false. See the documentation at http://docs.jquery.com/Ajax/jQuery.ajax#options (click options tab to see them).
(async () => {
for(f of ['1.json','2.json','3.json']){
var json = await $.getJSON(f);
console.log(json)
};
})()
requests 3 json files with jQuery ajax calls
process in sequence (not in parallel) with await
works in Chrome/Firefox/Edge (as of 1/30/2018)
more at MDN
The best way you could do this is by chaining callbacks as Nosredna said. I wouldn't recommend using synchronous XMLHttpRequest as they lock your entire application.
There aren't much helper for this as far as I know, but you could do something resembling a callback FIFO.
You could give narrative javascript a try http://www.neilmix.com/narrativejs/doc/
I've never used it myself though. If I wanted to do this, I would setup some kind of abstraction for chaining asynchronous actions. As others have said, the synchonous version of the ajax object blocks events from being processed while it's waiting for a response. This causes the browser to look like it's frozen until it recieves a response.
Set the async option to false, e.g.,
$.ajax({ async: false /*, your_other_ajax_options_here */ });
Reference: Ajax/jQuery.ajax
You can use promise to make ajax calls sequential. Using Array push and pop promise method, sequential ajax calls will be lot easier.
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(id) {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/'+id,
dataType:'json',
success: function(data)
{
console.log("Ajax Request Id"+id);
console.log(data);
resolve();
}
});
});
}
function pushPromise(id)
{
promises.push(promises.pop().then(function(){
return methodThatReturnsAPromise(id)}));
}
pushPromise(1);
pushPromise(3);
pushPromise(2);
Look at this: http://docs.jquery.com/Ajax/jQuery.ajax (click on the "options" tab).
But remember a synchronous call will freeze the page until the response is received, so it can't be used in a production site, because users will get mad if for any reason they have to wait 30 seconds with their browser frozen.
EDIT: ok, with your update it's clearer what you want to achieve ;)
So, your code may look like this:
$.getJSON("http://example.com/jsoncall", function(data) {
process(data);
$.getJSON("http://example.com/jsoncall2", function (data) {
processAgain(data);
$.getJSON("http://example.com/anotherjsoncall", function(data) {
processAgainAndAgain(data);
});
});
});
This way, the second call will only be issued when the response to the first call has been received and processed, and the third call will only be issued when the response to the second call has been received and processed. This code is for getJSON but it can be adapted to $.ajax.
The modern way of sequencing jQuery asynchronous operations is to use the promises they already return and the flow control that promises support and this is not currently shown in any of the other answers here from prior years.
For example, let's suppose you wanted to load several scripts with $.getScript(), but the scripts must be loaded sequentially so the second one doesn't load/run until the first has finished and so on and you want to know when they are all done. You can directly use the promise that $.getScript() already returns. For simplicity, you can await that promise in a for loop like this:
async function loadScripts(scriptsToLoad) {
for (const src of scriptsToLoad) {
await $.getScript(src);
}
}
loadScripts([url1, url2, url3]).then(() => {
console.log("all done loading scripts");
}).catch(err => {
console.log(err);
});
Since all jQuery Ajax-related asynchronous operations now return promises (and have for many years now), you can extend this concept to any of jQuery's Ajax-related operations.
Also, note that all the other attempts in other answers here to wrap a jQuery operation in a new promise or in a jQuery deferred are obsolete and considered a promise anti-pattern because when the operation itself already returns a promise, you can just use that promise directly without trying to wrap it in your own new promise.
Synchronous calls aren't necessarily slower, if you have an app where AJAX calls open, posts to, then closes a socket, multiple calls to the socket don't make sense as some sockets can only handle a single connection, in which case, queuing data so its only sent when the previous AJAX call has completed means much higher data throughput.
How about using Node.js events?
var EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var $ = require('jquery');
var doSomething = function (responseData) {
var nextRequestData = {};
// do something with responseData
return nextRequestData;
};
// ajax requests
var request1 = $.ajax;
var request2 = $.ajax;
var requests = [request1, request2];
eventEmitter.on('next', function (i, requestData) {
requests[i](requestData).then(
function (responseData) {
console.log(i, 'request completed');
if (i+1 < requests.length) {
var nextRequestData = doSomething(responseData);
eventEmitter.emit('next', i+1, nextRequestData);
}
else {
console.log('completed all requests');
}
},
function () {
console.log(i, 'request failed');
}
);
});
var data = {
//data to send with request 1
};
eventEmitter.emit('next', 0, data);
sequential != synchronous, and I would like an asynchronous and sequential solution
Synchronous execution generally means "using the same clock", while sequential execution means "following in order or sequence".
For your specific use case I think both conditions must be met, as asynchronous execution implies the possibility of a non-sequential result.
I have run into quite an annoying issue with my Javascript code due to its synchronous nature, in short the problem is I need to dump a load of data into an array.
This array is then written directly into a JSON file.
function main(callback) {
myarrary = []
//run some SQL query via stored procedure
for (result in query) {
myarray.push({
//my data is entered here
})
}
let data = JSON.stringify({myarray: somedata}, null, 4)
fs.writeFileSync('testingjson.json', data)
callback();
}
main(another_function)
My issue is that the data dump and callback are executed immediately, as such no data is transferred to the file and 'another_function` relies on data being there so that doesn't work either.
Therefore I need the dump to wait for the loop to finish appending myarray
I have also tried entering the dump and callback into my for loop however they execute on the first iteration.
Any help is appreciated!
Functionally your current code looks fine, except that you should not call the function as callback unless it is an asynchronous function.
Callbacks are usually associated with asynchronous API's. It is useful if you want main(...) to return immediately so that you can just continue with something else. Since that is the default behavior, the API is
fs.writeFile(filename, data[, options], callback)
So to convert your program to async, you need:
fs = require("fs");
function main(callback) {
// Oops. Why is query executed synchronously?
query = sqlSync() ;
fs.writeFile('delete.json', "Data", callback);
}
main(() => console.log("Done"))
You can also convert SQL calls to async if your API's support that (since it is an I/O operation).
Once you are comfortable with normal callbacks, you can try using promise or async-await as mentioned here.
if you have two methods who are returning callbacks you can use Promise.all([]). Here in Promise.all() you can write your database calls or other functions which are resolving/rejecting anything. From there you can get them in res object.
Trying to make call to multiple asynchronous function, and due to which getting result as undefined.
Tried async.waterfall, however not able to make it work.
Code:
const pendingData = [];
async.waterfall([
function (callback) {
WaitForApproval.find({}, (err,result) => {
callback(null,result);
});
},
function (result, callback) {
result.forEach(data => {
User.findOne({_id: data.uploadedBy}, (err,name) => {
let total = {
id: data._id,
name: name.name,
subject: data.subject,
uploadOn: data.uploadedAt
};
pendingData.push(total);
});
});
callback(null,'done');
}
], function (err,result) {
if(result === 'done') {
console.log(pendingData); // it is giving empty result.
}
});
How to wait for asynchronous function?
The issue you are having is that you are async functions within a non-async forEach loop.
You have a few options here:
Make the mongoDB call recursively - wrap this query in a function that calls itself after the query returns.
Read about mongoDB batch operations - https://docs.mongodb.com/manual/reference/method/Bulk/
Use an async/await pattern with each call by declaring the callback function within the async waterfall as 'async' and then using 'await' inside of the function for each query. Out of the box, forEach is not async. If you want to still use forEach, you can either re-write it async (See below) or use a regular for loop:
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
** There are additional ways to solve this beyond what is posted here, but here's a few things that will work if implemented correctly.
I would suggest you add the call for callback(null,'done'); immediately pendingData.push(total);
you are right, the async nature is making things hard for you now, but suppose you used Promises and chain them together, that would save you a lot of trouble.
once a time I had a similar problem of asynchronous code running out of order I wanted so I made a little Tweak using a custom promise function(I mead up) and call it order..such Idea can solve your problem if you could apply it to your code properly
https://github.com/lalosh/Ideas/blob/master/promiseOrder.js
I have a strict JavaScript API naming scheme I need to follow, it looks like this:
var Items = function() {
this.items = [];
};
Items.prototype.get() {
db.items.find(function(err, items) {
this.items = items;
});
return this.items;
}
The problem is the async call (db.items.find..) that doesn't have time to finish before the get() method returns an empty this.items..
The client needs to make the calls like this:
items = new Items();
console.log(items.get());
What's best practice to handle async calls here while still strictly following the API naming scheme?
Is there some native way I can let get() wait for a return inside the callback or do I need some kind of async lib for this?
EDIT:
Apparently what you are looking for might be possible using wait.for (https://github.com/luciotato/waitfor). I have not used it though, so I am not sure how well it would suit your needs. The method you would need would be wait.forMethod.
Previous Answer:
There is no way you can write async code in a synchronous manner. Also, it is not a good idea to mix async and sync methods. You are trying to define a synchronous method Item.prototype.get, but inside that you are using an async method db.items.find, which makes Item.prototype.get an asynchronous function. In order to get this to work you will have to define Item.prototype.get as a proper async function with a callback.
Items.prototype.get(fn) {
db.items.find(function(err, items) {
return fn(err, items);
});
}
You could then call it as
items = new Items();
items.get(function(err, items){
console.log(items);
}
I managed to solve this by using SilkJS (http://www.silkjs.net/), which is similar to Node in that it is built atop of V8 but it runs in synchronous mode.
This way I managed to keep the given API spec.