AJAX - How to make Asynchronous request one by one without freeze browser? - javascript

i have a textarea like below:
<textarea name="mailist" id="mailist" placeholder="email#domain.com"></textarea>
I want to make a request from the mailist, and send the data (email-per-line) to some URL using "split" and "foreach" . And it success.
But the problem is, when i click the submit button, the all request sent at the same time. How can i make it to:
Send the request after the end of the other
Make a request Asynchronously
Not freeze the browser

What you are looking for is called a promise. Every $.ajax function in jQuery returns a promise. You can read the documentation here to get more details. The gist is, the returned promise exposes a function called then, which can be used to chain requests one after the other, in the manner you desire.
Consider an ajax request of this sort
var request = $.ajax({
method: 'get',
url: 'www.google.com'
});
Since it runs asynchronously, you need to register handlers to let you know when the request is completed successfully, or failed, like so
request.then(
function successHandler(response) {
/* The first function will be called if the request is successful */
},
function failureHandler(response) {
/* The second function will be called if the request fails */
}
);
Using this pattern, you could wait for a request to either succeed or fail, before proceeding to the next one.
Leaving the earlier code section intact
You need to implement a queue of sorts. Most browsers have a sane limit of about 2-5 simultaneous requests. An example would be
var emailAddresses = [/* a list email addresses */];
var MAX_SIMULTANEOUS_REQUESTS = 4;
var CURRENT_REQUESTS = [];
function runRequests(emailAddresses, max, running) {
while(running.length < max) {
running.push(createRequest(emailAddress.pop(), running));
}
return emailAddresses.length;
}
function createRequest(emailAddress, running) {
var request = /* Creates requests in some manner */;
request
.then(
function() {
/* Remove this request from the list of running requests */
var indexOfRequest = running.indexOf(request);
running.splice(indexOfRequest, 1);
},
function() {
/* For extra points, add this to a retry queue */
}
);
return request;
}
/* Usage */
var interval = setInterval(function() {
var remaining = runRequests(emailAddresses, MAX_SIMULTANEOUS_REQUESTS, CURRENT_REQUESTS);
if (remaining === 0) {
clearInterval(interval);
}
}, 500);

Related

I would like to execute regular function in async way

Consider this sample (say this is module)
function Calculator(value){
return {
add: function(value2){
return: {
value: function(){
return value + value2;
}
}
}
}
}
This is a class, and requires an argument when initialization, sample usage:
var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).value(); // === 3;
Which is obviously expected, what i want is to execute add function in async way, just like that
var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).value() ==== 3 // this executes in 2secs (async)
// and then returns result
I would like to patch Calculator.add method so that it can work with async
function patch(module){ //module is Calculator class
var oldAdd = Calculator.add;
Calculator.add = function(){
// some magic
// trigger event or whatever
oldAdd.apply(Calculator, arguments);
}
}
INDEX.JS
var Calculator = require('calculator_app_module');
var calc = new Calculator(1);
calc.add(2).value() === 3; // equalize within 2 seconds
// after async call is done
calc.add(2).value().equal(3); // also legit
The problem is that calc.add(n) returns new function value which is undefined in async call, is there a way to get the calling fn of add and call it back when result comes
update
Prior to #Zohaib Ijaz answer, you cannot modify content/logic of package, only extend/patch, Package must return same API but in promise way, no code breaking
calc.add(2).value() === 3; // sync code
calc.add(2).value() === 3; // async code
calc.add(2).value().equal(3); // async code
How to achieve
update
According to #Zohaib Ijaz comment, this also legit
myCalc.add(2).value().equal(3); //async
Point is in converting sync to async without breaking package, but extending the outcome
If you request a result by calling a chain of methods, like this:
a = myCalc.add(2).value();
or this:
myCalc.add(2).value().equal(3);
then there is no possibility to retrieve and use results that become available only asynchronously (i.e. later, after the statement has been evaluated). Note that asynchronous involves some event being put in the event queue. The currently executing code must finish first (i.e. until the call stack is empty), before that event can get processed.
The above syntax is useful for immediate evaluation only. In order to process asynchronous results you need to provide a call-back function somewhere for being informed about those results.
So with an asynchronous dependency in the add method, your code could provide a callback to the add method, which it would call when it has received the asynchronous result:
myAsyncCalc.add(2, function (added) {
a = added.value();
});
Or, when using promises (which is really nice to work with), the add method would return an object to which you can assign the same call-back:
myAsyncCalc.add(2).then(function (added) {
a = added.value();
});
Note that the callback function is not part of the currently executing code. It just is a function reference, that can be used at a later, asynchronous event for calling you back. But that will be part of a separate execution sequence, that only starts when the internal event queue has been processed and an event has been processed that triggered that execution sequence.
If this is not an acceptable solution, and you really need the former syntax to somehow take an asynchronous produced result into account, then you are without hope: it is not possible, because that really represents synchronous code execution.
Wrapping your Object
You write that you cannot modify the content of the package, but can only extend it.
One way to do that is to make use of proxies.
The idea is that you trap a reference to the add method, and return
your own adapted version of the method, which can optionally still call the original method.
See the above referenced MDN article for examples.
Calling HTTP request Synchronously
If you really want to write code like this:
a = myCalc.add(2).value();
even when the implementation of add performs an HTTP request, then you could have a look at making the HTTP request synchronously. But it should be noted that this is considered bad practice.
Code Example
Here is code that performs the addition in three ways:
unmodified (synchronous)
with an asynchronous HTPP call
with a synchronous HTTP call
For the two modified versions, a proxy pattern is used. For the asynchronous example, a call back is used using the Promise pattern.
Code:
// code in module is not modified
function Calculator(value){
return {
add: function(value2){
return {
value: function(){
return value + value2;
}
}
}
}
}
// standard object creation
var myCalc = new Calculator(1); // initialized with 1
// Create a proxy for the above object, which will expose
// an asynchronous version of the "add" method. Note that the
// "myCalc" object is not modified.
var myCalcHttpAsync = new Proxy(myCalc, {
get: function(myCalc, name) {
if (name !== 'add') return myCalc[name]; // pass-through
return function(value2) {
return new Promise(function(resolve, reject) {
// Define some url
var url = 'http://api.stackexchange.com/2.2';
// Perform HTTP request
var request = new XMLHttpRequest();
// Define call back for when response becomes available
request.onload = function() {
if (request.readyState !== 4) return;
// When async task notifies it has finished:
// call the original "add" method and notify those
// waiting for the promise to get resolved
resolve(myCalc.add(value2));
};
// `true` as third argument makes the request asynchronous
request.open('GET', url, true);
request.send(null);
});
};
}
});
// Create another, alternative proxy for demonstrating
// synchronous HTTP call:
var myCalcHttpSync = new Proxy(myCalc, {
get: function(myCalc, name) {
if (name !== 'add') return myCalc[name]; // pass-through
return function(value2) {
// Define some url
var url = 'http://api.stackexchange.com/2.2';
// Perform HTTP request
var request = new XMLHttpRequest();
// `false` as third argument makes the request synchronous
request.open('GET', url, false);
// code execution "hangs" here until response arrives
request.send(null);
// process response...
var data = request.responseText;
// .. and return the value
return myCalc.add(value2);
};
}
});
// I/O
var std = document.getElementById('std');
var async = document.getElementById('async');
var sync = document.getElementById('sync');
// 1. Standard
std.textContent = myCalc.add(2).value();
// 2. Asynchronous HTTP
myCalcHttpAsync.add(2).then(function (added) {
// This needs to happen in a callback, otherwise it would be synchronous.
async.textContent = added.value();
});
// 3. Synchronous HTTP
sync.textContent = myCalcHttpSync.add(2).value();
Unmodified result: <span id="std">waiting...</span><br>
Result after asynchronous HTTP call: <span id="async">waiting...</span><br>
Result after synchronous HTTP call: <span id="sync">waiting...</span><br>
Here is my solution using promise.
Here is a link to jsbin where you can execute the code.
http://jsbin.com/qadobor/edit?html,js,console,output
function Calculator(value) {
return {
add: function(value2) {
return new Promise(function(resolve, reject) {
setTimeout(
function() {
resolve(value + value2);
}, 2000);
});
}
};
}
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).then(function(ans){
// this callback will be called after 2 seconds after promise resolve.
console.log(ans);
});

Webworker return result after callback

I have an Angular service where I'm using $q service in combination with webworkers. In my original function before using webworkers, my completeClass function would return an object.
I replaced the code to post a message to my new web worker script.
The callback of the webworker is in my initWorkers function where I add the eventlistener.
My goal is that the completeClass function returns the result of the webworker. How can I make this happen?
this.classWorker = new Worker('app/shared/autocomplete/autocomplete-class-worker.js');
this.completeClass = function(text){
var self = this;
var defer = $q.defer();
classWorker.postMessage([text, this.oldText, this.oldProposals, this.aliases, this.entityClasses])
};
this.initWorkers = function(){
var self = this;
worker.addEventListener('message', function(e) {
defer.resolve(e.data);
self.oldProposals = e.data[1];
self.oldText = text;
return e.data[0];
}, false);
};
If you are going to call the worker when the previous call is still running, then you need something to either queue or keep track of in-progress requests. My suspicion is that unless you need control of the queue of requests, it's simpler for UI thread to fire off the requests to the worker, so the browser essentially queues the requests for you.
But you would still need to keep track of the requests sent somehow, so when you get a message back from the worker, you know which one it is responding do. You can do this by, in the main thread
Generating a unique ID for each request. An ever-increasing integer can be enough for a lot of cases.
Creating a deferred object and storing it, associated with the ID
Firing off the request to the worker, passing the ID
Passing a the promise of the deferred object back to the caller
The worker then
Receives the message, with the ID
Does its' work
Posts the result back, along with the ID
The main thread then
Receives the message, with the ID and result of the work.
Retrieves the deferred object by the ID, and resolves it with the results of the work.
To do this, you can use some code like below. I've slightly simplified your case by passing just text to the worker, getting completeText back. You can add more information going either way in a real case.
app.service('AutoComplete', function($q) {
var id = 0;
var worker = new Worker('app/shared/autocomplete/autocomplete-class-worker.js');
var inProgress = {};
this.completeClass = function(text) {
var deferred = $q.defer();
id++;
var request = {
id: id,
text: text
};
inProgress[id] = deferred;
worker.postMessage(request);
return deferred.promise;
};
worker.onmessage = function(e) {
var response = e.data;
var id = response.id;
var type = response.type; // resolve, reject, or notify
var completeText = response.completeText;
inProgress[id][type](completeText);
if (type === 'resolve' || type === 'reject') {
delete inProgress[id];
}
};
});
Then in the worker you can have code like:
self.onmessage = function(e) {
var request = e.data;
var text = request.text;
var id = request.id;
// Do the work here
var completeText = ...
var response = {
id: id,
type: 'resolve', // Can reject the promise in the main thread if desired
completeText: completeText
};
self.postMessage(response);
};
My goal is that the completeClass function returns the result of the webworker. How can I make this happen?
To clarify, it can't directly return the result, because the result is calculated asynchronously, and all function calls must return synchronously. But it can return a promise that resolves to the result later, just like $http does for example. To use the above, you can do something like
app.controller('MyController', function($scope, AutoComplete) {
$scope.complete = function(text) {
AutoComplete.completeClass(text).then(function(result) {
// Do something with result
});
});
});
Note: technically, passing an ID along with each request isn't necessary if the worker does all its' work synchronously on one request, and so responds to each call in the order received. In the above example, the main thread can always assume the calls to the worker make a first-in-first-out queue. However, passing an ID gives the flexibility of the worker not finishing the work in the order received. Say in a later version it needs to do something asynchronous, like call another worker, or make an ajax request, this method will allow that.

Prevent multiple AJAX calls to the same url within timeframe

I have an app which wants to get info about every marker on a map.
Each marker has a class, such as "car" or "pedestrian".
The app makes (via jQuery) a getJSON call to "http://myserver/info/".
However, since multiple markers may have the same class, the server could end up getting hit with many requests.
Accordingly, I'd like to pool requests which occur within a specified time frame (maybe 5 seconds or so) so that only one request is made, but each calling instance of getJSON is unaware of it.
My thought is to wrap getJSON in another function which stores the URLS in a hashmap/dictionary and stores up promises for each requester. When data is returned, the promises are fulfilled.
I ask, is there a standard way of doing this (debouncing an AJAX request, as it were)?
I created something (in 25 minutes ^^) that might help you; it's a Timeout manager:
var requestsPool = {
requests: {}, //list of urls
timeout: 5000, //In milliseconds
add: function(url) {
if(requestsPool.exists(url)) return false; //check if url is already present in the pool
requestsPool.requests[url] = setTimeout(function(u) {
requestsPool.remove(u);
}.bind(this, url), requestsPool.timeout); //Defining the timeout
return true;
},
exists: function(url) {
return requestsPool.requests[url]; //Return the Timeout ID if present or undefined
},
remove: function(url) {
return delete requestsPool.requests[url]; //return true almost always #link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
},
cancel: function(url) {
clearTimeout(requestsPool.requests[url]); //cancel the timeout
return requestsPool.remove(url); //remove the url form the pool
}
}
$(anchor).click(function() {
if(requestsPool.exists(anchor.href)) {
// If cooldown is present
} else {
$.getJSON(anchor.href, function(data) {
requestsPool.add(anchor.href);
});
}
})
My thought is to wrap getJSON in another function which stores the URLS in a hashmap/dictionary and stores up promises for each requester
Yes, that's a good idea. It might look like this:
var debouncedGet = (function() {
var pool = {};
return function get(url) {
if (!pool[url]) {
pool[url] = $.getJSON(url);
setTimeout(function() {
pool[url] = null;
}, 5000); // you might want to move this into a `pool[url].always(…)` callback
// so the timer starts when the request returned
}
return pool[url];
};
}());
Here's my bid:
(function(window,$,undefined){
'use strict';
var cache = {},
timeout = 5e3;
// Use like traditional $.getJSON
$.getJSON = function(url,data,callback){
if ($.isFunction(data)){
callback = data;
data = undefined;
}
// Establish a cache key so we can re-reference existing
// requests to subsequent ones (within the timeout window).
var cacheKey = url;
if (cache[cacheKey]){
// This is an existing request; Simple add the callback
// onto the promise and return it.
return cache[cacheKey].done(callback);
} else {
// This is a new request. Build up a new request,
// attach the callback to the promise, and also add
// a couple cleanup methods for disposing the cache
// when appropriate.
cache[cacheKey] = $.ajax($.extend({
url: url,
type: 'get',
dataType: 'json',
data: data,
}, $.isPlainObject(url) && url))
.done(callback)
.always(function(){
delete cache[cacheKey];
});
setTimeout(function(){
// TODO: Probbaly want to store a reference to
// this timeout and clear it in the .always (to
// avoid race condition between .always firing
// and new request coming in but not returning yet)
cache[cacheKey] && delete cache[cacheKey];
}, timeout);
return cache[cacheKey];
}
};
})(window,jQuery);
And, FWIW, a jsFiddle: http://jsfiddle.net/ajtbdxt7/

New $.Deferred object with the old callbacks

Please forgive me if this is a stupid question. I have been trying for hours and my brain have just stopped working.
I have such system that consists of three AJAX calls. Server response of first call usually is a 200 Success; but second and third queries are fragile because they are image uploading, and on the server side, I have so much validation rules that client's images mostly fail.
window.AjaxCall = function () {
// to pass to $.ajax call later
this.args = arguments;
// xhr status
this.status = null;
// xhr results (jqXHR object and response)
this.xhrResponse = {};
this.dfr = new $.Deferred();
// to provide an easier interface
this.done = this.dfr.done;
this.fail = this.dfr.fail;
this.then = this.dfr.then;
};
AjaxCall.prototype.resetDfr = function () {
this.dfr = new $.Deferred();
};
AjaxCall.prototype.resolve = function () {
this.dfr.resolve(
this.xhrResponse.result,
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.reject = function () {
this.dfr.reject(
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.query = function () {
var _this = this;
// if query hasn't run yet, or didn't return success, run it again
if (_this.status != 'OK') {
$.ajax.apply(_this, _this.args)
.done(function (result, textStatus, jqXHR) {
_this.xhrResponse.result = result;
_this.xhrResponse.jqXHR = jqXHR;
_this.resolve();
})
.fail(function (jqXHR) {
_this.xhrResponse.jqXHR = jqXHR;
_this.reject();
})
.always(function (a, b, c) {
var statusCode = (typeof c !== 'string'
? c
: a).status;
if (statusCode == 200) {
_this.status = 'OK';
}
});
}
// if query has been run successfully before, just skip to next
else {
_this.resolve();
}
return _this.dfr.promise();
};
AjaxCall class is as provided above, and I make the three consecutive calls like this:
var First = new AjaxCall('/'),
Second = new AjaxCall('/asd'),
Third = new AjaxCall('/qqq');
First.then(function () {
console.log('#1 done');
}, function() {
console.error('#1 fail');
});
Second.then(function () {
console.log('#2 done');
}, function() {
console.error('#2 fail');
});
Third.then(function () {
console.log('#3 done');
}, function() {
console.error('#3 fail');
});
var toRun = function () {
First.query()
.then(function () {
return Second.query();
})
.then(function () {
return Third.query()
});
};
$('button').click(function () {
toRun();
});
Those code are in a testing environment. And by testing environment, I mean a simple HTML page and basic server support for debugging.
Home page (/) always returns 200 Success.
/asd returns 404 Not Found for the first 3 times and 200 Success once as a pattern (i.e. three 404s -> one 200 -> three 404s -> one 200 -> three 404s -> ... ).
/qqq returns 404 Not Found all the time.
When I click the only button on the page, first query returns success and second fails as expected. When I click the button second time, first query skips because it was successful last time and second fails again, also as expected.
The problem here is:
before I used the resetDfr method because the dfr is alreay resolved or rejected, it doesn't react to resolve and reject methods anymore.
When I call the resetDfr method in the way I show in the example, dfr is able to get resolved or rejected again, but the callbacks of the old dfr are not binded with the new dfr object and I couldn't find a way to clone the old callbacks into the new dfr.
What would be your suggestion to accomplish what I'm trying to do here?
Promises represent a single value bound by time. You can't conceptually "reuse" a deferred or reset it - once it transitions it sticks. There are constructs that generalize promises to multiple values (like observables) but those are more complicated in this case - it's probably better to just use one deferred per request.
jQuery's AJAX already provides a promise interface. Your code is mostly redundant - you can and should consider using the existent tooling.
Let's look at $.get:
It already returns a promise so you don't need to create your own deferred.
It already uses the browser cache, unless your server prohibits HTTP caching or the browser refuses it only one request will be made to the server after a correct response arrived (assuming you did not explicitly pass {cache: false} to its parameters.
If making post requests you can use $.post or more generally $.ajax for arbitrary options.
This is how your code would roughly look like:
$("button").click(function(){
var first = $.get("/");
var second = first.then(function(){
return $.get("/asd");
});
var third = second.then(function(){
return $.get("/qqq");
});
});
The reason I put them in variables is so that you will be able to unwrap the result yourself later by doing first.then etc. It's quite possible to do this in a single chain too (but you lose access to previous values if you don't explicitly save them.
For the record - it wasn't a stupid question at all :)

Execute a forEach like a waterfall in async

I'm trying to retrieve longitude and latitude from a list of addresses with the Google API via a Node.js script. The call itself works fine but since I have around 100 addresses to submit. I use a async.forEach on an array, but the calls are made too fast and I get the error "You have exceeded your rate-limit for this API."
I found that the number of calls is limited to 2500 every 24h and maximum 10 a second. While I'm OK for the 2500 a day, I make my calls way too fast for the rate limit.
I now have to write a function who will delay the calls enough not to reach the limit. Here is a sample of my code :
async.forEach(final_json, function(item, callback) {
var path = '/maps/api/geocode/json?address='+encodeURIComponent(item.main_address)+'&sensor=false';
console.log(path);
var options = {
host: 'maps.googleapis.com',
port: 80,
path: path,
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}
// a function I have who makes the http GET
rest.getJSON(options, function(statusCode, res) {
console.log(res);
callback();
});
}, function() {
// do something once all the calls have been made
});
How would you proceed to achieve this? I tried putting my rest.getJSON inside a 100ms setTimeout but the forEach iterates through all the rows so fast that it starts all the setTimeout almost at the same time and therefore it doesn't change anything...
The async.waterfall looks like it would do the trick, but the thing is I don't know exactly how many rows I will have, so I can't hardcode all the function calls. And to be honest, it would make my code really ugly
The idea is that you can create a rateLimited function that acts much like a throttled or debounced function, except any calls that don't execute immediately get queued and run in order as the rate limit time period expires.
Basically, it creates parallel 1 second intervals that self-manage via timer rescheduling, but only up to perSecondLimit intervals are allowed.
function rateLimit(perSecondLimit, fn) {
var callsInLastSecond = 0;
var queue = [];
return function limited() {
if(callsInLastSecond >= perSecondLimit) {
queue.push([this,arguments]);
return;
}
callsInLastSecond++;
setTimeout(function() {
callsInLastSecond--;
var parms;
if(parms = queue.shift()) {
limited.apply(parms[0], parms[1]);
}
}, 1010);
fn.apply(this, arguments);
};
}
Usage:
function thisFunctionWillBeCalledTooFast() {}
var limitedVersion = rateLimit(10, thisFunctionWillBeCalledTooFast);
// 10 calls will be launched immediately, then as the timer expires
// for each of those calls a new call will be launched in it's place.
for(var i = 0; i < 100; i++) {
limitedVersion();
}
Here's how I would hack it (Note: arr is your array of locations):
function populate(arr, callback, pos) {
if(typeof pos == "undefined")
pos=0;
var path = '/maps/api/geocode/json?address='+encodeURIComponent(arr[pos].main_address)+'&sensor=false';
console.log(path);
var options = {
host: 'maps.googleapis.com',
port: 80,
path: path,
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}
// a function I have who makes the http GET
rest.getJSON(options, function(statusCode, res) {
console.log(res);
});
pos++;
if(pos<arr.length)
setTimeout(function(){
populate(arr,callback,pos);
},110); //a little wiggle room since setTimeout isn't exact
else
callback();
}
You could add a rate limiting function, but, IMHO, it introduces unnecessary complexity. All you really want to do is call the function every tenth of a second or so until you're done with your list, so do that.
It's certainly not as extensible as the alternative, but I'm a fan of simplicity.

Categories