I'm hoping I can get my problem across.
I have a locally developed JS library called ConfirmDialog. ConfirmDialog has two callbacks: onConfirmYes() and onConfirmNo(). Here's a usage example:
ConfirmDialog.onConfirmYes() = function() {
// make a jQuery ajax call
}
ConfirmDialog.onConfirmNo() = function() {
// make a different ajax call
}
ConfirmationDialog.confirm("Are you sure you wish to do this?");
What I need to do is chain this together using jQuery Deferred/Promise.
var confirmTraining = false;
var confirmNextAvailDate = false;
function confirmTraining() {
ConfirmDialog.onConfirmYes() = function() {
// make a jQuery ajax call to accept training,
// successful ajax call sets confirmTraining
// to true
}
ConfirmDialog.onConfirmNo() = function() {
// make a different ajax call to decline training,
// successful ajax call sets confirmTraining to
// false
}
ConfirmationDialog.confirm("Do you wish to accept training?");
}
function confirmNextAvailableTraining() {
if (confirmTraining == true) {
ConfirmationDialog.onConfirmYes() = function() {
// ajax call to reserve training, success sets
// sets confirm next date to true
}
ConfirmationDialog.onConfirmNo() = function() {
// ajax call to put person on waitlist
}
ConfirmationDialog.confirm("Do you wish for the next available class?");
}
}
// so on
The second method will require access to the boolean value set by the result of an ajax call made by the first method.
So, here's what I need to do:
// wrong syntax, I know
confirmTraining().then(confirmNextAvailableTraining).finally(function() {
// whatever else needs doing
}
The problem I have is that since the variables are not set until the callback fires for the ConfirmDialog, I cannot figure out how to set up the Deferred/Promise for the methods I wish to chain.
Can anyone help?
Jason
Look like you will have to actually use a promise object inconfirmDialog.onConfirmYes() and confirmDialog.onConfirmNo().
ConfirmDialog.onConfirmYes = function() {
//create a promise object that will make the call
// return the promise object
}
ConfirmDialog.onConfirmNo = function() {
//create a promise object that will make the call
// return the promise object
}
function confirmTraining() {
var aPromiseobj;
if(logic to determine what was picked)
aPromiseobj = ConfirmDialog.onConfirmYes();
}else{
aPromiseobj = ConfirmDialog.onConfirmNo()
}
return aPromiseobj;
}
Now when you called then it will be on a promise object
confirmTraining().then(confirmNextAvailableTraining).finally(function() {
// whatever else needs doing
}
Related
Hi I am using Jquery and ember to delete certain elements ,I want to use Deferred objects to stop the code and then next statements has to be executed
Here KillMany is Function once it is called it will execute array.forEach(tryKill);
statement
that contains an array of elemets[array contains 100 elements inside for each each time a call back is calling to delete the each element from server]
Here I want to execute my code after completely finishing [deletion of elements] myFinalblock callback has to be calle
please guide me
killMany: function(c) {
var t = this
, wait = []
, dfd = new $.Deferred();
function keep(tile) {
tile.setProperties({ isSelected: false, isHidden: false });
}
function destroy(tile) {
if (t.get('reports')) {
t.get('reports').removeObject(tile.entity);
}
tile.remove.bind(tile);
}
function tryKill(tile) {
tile.set('isHidden', true);
tile.get('entity').kill()
.then(destroy.bind(null, tile),
keep.bind(null, tile));
}
function myFinalblock(){
this.set('selectedTiles', []);
}
this.set('promptDestroyMany', false);
if (c.response) {
var array = this.get('selectedTiles');
array.forEach(tryKill);
myFinalblock();
}
},
You seem to be missing the point of promises a bit. They do not "stop" your code. They allow you to cleanly route your code's asyncrhonous functionality. Therefore you need a way to wait until all of the tryKill calls are done before calling myFinalBlock. To do this, you first need to modify your tryKill function to return its promise:
function tryKill(tile) {
tile.set('isHidden', true);
return tile.get('entity')
.kill()
.then(destroy.bind(null, tile),
keep.bind(null, tile));
}
Then you can do this:
var tiles = this.get('selectedTiles');
$.when.apply($, tiles.map(tryKill))
.then(myFinalBlock)
.done();
On a side note, I suggest looking into a proper promise library and not using jQuery's built-in deferreds, as they are known to have a number of problems.
I'm trying to work through this js/async scenario and i'm trying to know how the rest of the js world handles this.
function doStuff(callback) {
cursor.each(function(err, blahblah) {
...doing stuff here takes some time
});
... Execute this code ONLY after the `cursor.each` loop is finished
callback();
EDIT
Here's a more concrete example updated using most of the suggestions below which still doesn't work.
function doStuff(callback) {
MongoClient.connect(constants.mongoUrl, function(err, db) {
var collection = db.collection('cases2');
var cursor = collection.find();
var promises = []; // array for storing promises
cursor.each(function(err, item) {
console.log('inside each'); // NEVER GETS LOGGED UNLESS I COMMENT OUT THIS LINE: return Q.all(promises).then(callback(null, items));
var def = Q.defer(); // Create deferred object and store
promises.push(def.promise); // Its promise in the array
if(item == null) {
return def.resolve();
}
def.resolve(); // resolve the promise
});
console.log('items'); // ALWAYS GETS CALLED
console.log(items);
// IF I COMMENT THIS LINE OUT COMPLETELY,
// THE LOG STATEMENT INSIDE CURSOR.EACH ACTUALLY GETS LOGGED
return Q.all(promises).then(callback(null, items));
});
}
without using promises or any other dependencies/libraries you can simply
function doStuff(callback) {
add a counter
var cursor = new Array(); // init with some array data
var cursorTasks = cursor.length;
function cursorTaskComplete()
{
cursorTasks--;
if ( cursorTasks <= 0 ) {
// this gets get called after each task reported to be complete
callback();
}
}
for ( var i = 0; i < cursor.length; i++ ) {
...doing stuff here takes some time and does some async stuff
check after each async request
...when async operation is complete call
cursorTaskComplete()
}
}
Without knowing the details of the async calls you're making within the cursor.each loop, I shall assume that you have the ability to invoke a callback each time the functions invoked therein have completed their async task:
function doStuff() {
var promises = []; // array for storing promises
cursor.each(function(err, blahblah) {
var def = Q.defer(); // create deferred object and store
promises.push(def.promise); // its promise in the array
call_async_function(..., def.resolve); // resolve the promise in the async function's callback
});
// pass the array to Q.all, only when all are resolved will "callback" be called
return Q.all(promises);
}
and the usage then becomes:
doStuff().then(callback)
Note how the invocation of the callback now never touches the doStuff function - that function now also returns a promise. You can now register multiple callbacks, failure callbacks, etc, all without modifying doStuff. This is called "separation of concerns".
[NB: all the above based on the Q promises library - https://github.com/kriskowal/q]
EDIT further discussion and experimentation has determined that the .each call is itself async, and gives no indication to the outside when the last row has been seen. I've created a Gist that demonstrates a resolution to this problem.
if you want to do it with the async module, you can make use of the async forEachSeries function
Code snippet:
function doStuff(callback) {
async.forEachSeries(cursor, function(cursorSingleObj,callbackFromForEach){
//...do stuff which takes time
//this callback is to tell when everything gets over execute the next function
callbackFromForEach();
},function(){
//over here the execution of forEach gets over and then the main callback is called
callback();
});
}
In my mind an elegant/ideal solution would be to have something like
cursor.each(........).then( function() { ....your stuff});
But without that you can do this....UPDATED
http://plnkr.co/edit/27l7t5VLszBIW9eFW4Ip?p=preview
The gist of this is as shown below...notice....when
var doStuff = function(callback) {
cursor.forEach(function(cursorStep) {
var deferred = $q.defer();
var promise = deferred.promise;
allMyAsyncPromises.push(promise);
cursorStep.execFn(cursorStep.stepMeta);
promise.resolve;
});
$q.when(allMyAsyncPromises).then(callback);
}
After hitting the start button wait for few seconds...the async tasks have been simulated to finish in 5 seconds so the status will update accordingly.
Not having access to a real cursor object..I had to resort of fake cursor like and array.
I have a handler (callback), an object to handle and four functions, which collect the data to object. In my case I wish to asynchronously call four data retrievers and when execution of all four is complete, handle the resulting object (something similar to the following):
var data = {};
function handle (jsObj) {}
// data retrieving
function getColorData () {}
function getSizeData () {}
function getWeightData () {}
function getExtraData () {}
data.color = getColorData();
data.size = getSizeData();
data.weight = getWeightData();
data.extra = getExtraData();
handle( data );
Of course, this code will not work properly. And if I chain data retrieving functions, they will be called one after another, right?
All four functions should be called asynchronously, cause they are being executed for too long to call them one by one.
Updated:
Thanks to everybody for your suggestions! I prefered $.Deferred(), but I found it slightly difficult to make it work the way I need. What I need is to asynchronously make a view, which requires four kinds of data (extraData, colorData, sizeData & weightData) and I have three objects: App, Utils & Tools.
Just a small description: view is created by calling App.getStuff passed App.handleStuff as a callback. Callback in the body of App.getStuff is called only $.when(App.getExtraData(), App.getColorData(), App.getSizeData(), App.getWeightData()). Before that Utils.asyncRequest passed Tools.parseResponse as a callback is called.
So, now the question is should I create four deferred objects inside each App.get*Data() and also return deferred.promise() from each of them?
And should I deferred.resolve() in the last function in my order (Tools.parseResponse for App.getExtraData in my example)?
var view,
App,
Utils = {},
Tools = {};
// Utils
Utils.asyncRequest = function (path, callback) {
var data,
parseResponse = callback;
// do something with 'data'
parseResponse( data );
};
// Tools
Tools.parseResponse = function (data) {
var output = {};
// do something to make 'output' from 'data'
/* So, should the deferred.resolve() be done here? */
deferred.resolve(output);
/// OR deferred.resolve();
/// OR return output;
};
// App
App = {
// Only one method really works in my example
getExtraData : function () {
var deferred = new jQuery.Deferred();
Utils.asyncRequest("/dir/data.txt", Tools.parseResponse);
return deferred.promise();
},
// Others do nothing
getColorData : function () { /* ... */ },
getSizeData : function () { /* ... */ },
getWeightData : function () { /* ... */ }
};
App.getStuff = function (callback) {
$.when(
App.getExtraData(),
App.getColorData(),
App.getSizeData(),
App.getWeightData()
)
.then(function (extraData, colorData, sizeData, weightData) {
var context,
handleStuff = callback;
// do something to make all kinds of data become a single object
handleStuff( context );
});
};
App.handleStuff = function (stuff) { /* ... */ };
/// RUN
view = App.getStuff( App.handleStuff );
I did not expect the code in my example above to work, it is for illustrative purposes.
I've been trying to solve this for quiet a long time and it still gives no result. The documentation for jQuery.Deferred() and discussions around this, unfortunately, did not help me. So, I would be very glad and greatful for any help or advise.
Conceptually, you would use a counter that gets incremented as each asynchronous call completes. The main caller should proceed after the counter has been incremented by all the asynchronous calls.
I think what you're looking for are Promises / Deferreds.
With promises you can write something like:
when(getColorData(), getSizeData(), getWeightData(), getExtraData()).then(
function (colorData, sizeData, weightData, extraData) {
handle(/*..*/);
}
)
The get*Data() functions will return a promise that they fulfill when their assynchronous call is complete.
Ex:
function getData() {
var promise = new Promise();
doAjax("getData", { "foo": "bar" }, function (result) {
promise.resolve(result);
});
return promise;
}
The when simply counts the number arguments, if all it's promises are resolved, it will call then with the results from the promises.
jQuery has an OK implementation: http://api.jquery.com/jQuery.when/
What I could suggest for this scenario would be something like that.
write a function like this
var completed = 0;
checkHandler = function() {
if(completed == 4) {
handle(data);
}
}
where completed is the number of positive callbacks you must receive.
As soon as every function receives a callback you can increment the "completed" counter and invoke the checkHandler function. and you're done!
in example
function getColorData() {
$.get('ajax/test.html', function(data) {
completed++;
checkHandler();
});
}
I have a bunch of nested functions returning deferred objects from ajax calls. Here's what my code looks like
function makeCalls() {
var ajaxDfd1 = $.ajax(...);
ajaxDfd1.then(function() {
// want to execute after first call
var ajaxDfd2 = $.ajax(...);
ajaxDfd2.done(function() {
// want to execute after second call
});
return ajaxDfd2;
});
return ajaxDfd1;
}
makeCalls().done(function() {
// stuff here was executed early
});
But my calls are not being executed in the order I intend them to be. Stuff inside makeCalls().done() seems to be called before ajaxDfd2 is actually done.
Choc,
You've made an incorrect assumption in your answer; namely that $.ajax() returns a "regular object".
This is not correct.
$.ajax() returns a jqXHR object, which is a superset of a Promise, ie. it has all of Promise's methods plus others which are specific to AJAX (such as .abort()).
If the object returned by $.ajax() was not Promise-compatible, then the code in your question would throw an error on trying to execute makeCalls().done(...).
The reason why your .done() function executes first it that the function returns the outer jqXHR.
Here's a couple of ways to achieve the behaviour you were expecting :
function makeCalls() {
var dfrd = new Deferred();
$.ajax(...).then(function() {
$.ajax(...).done(dfrd.resolve);
});
return dfrd.promise();
}
makeCalls().done(function(data) {
// ...
});
or
function makeCalls(fn) {
$.ajax(...).then(function() {
$.ajax(...).done(fn);
});
}
makeCalls(function(data) {
// ...
});
You could even try :
function makeCalls() {
return $.ajax(...).then(function() {
return $.ajax(...);
});
}
makeCalls().done(function() {
// ...
});
I knew I must be getting something wrong, and spent a really long time staring at this:
When should I use jQuery deferred's "then" method and when should I use the "pipe" method?
Later I realised that I can't do
var dfdObj = $.ajax(...);
dfdObj.then(...);
return dfdObj;
This doesn't actually return the deferred object. It returns an Ajax object, and since it is a regular object, it is automatically classified as resolved and thus the .done() function was being called early. Instead, I need to do this if I want to return a var
var dfdObj = $.ajax(...).then(...);
return dfdObj;
Your code works like this:
function makeCalls() {
var ajax1 = $.ajax(…);
var ajax1and2 = ajax1.then(makeCall2);
function makeCall2() {
// want to execute after first call
var ajax2 = $.ajax(…);
ajax2.done(after2);
function after2() {
// want to execute after second call
});
return ajax2;
});
return ajax1;
}
var ajax1 = makeCalls();
ajax1.done(function() {
// stuff here was executed early
});
ajax1and2 is the result of the then/pipe call you wanted. A callback on it will wait for the result of makeCall2 (i.e. the ajax2) which waits for ajax1. It would be equal to the after2 callback.
Yet, your makeCalls function returns the ajax1 object (you can see I assigned it to a global ajax1 variable as well) and both the makeCall2 and the done-callback will be executed after your first ajax has finished.
So, instead you need to return the ajax1and2 deferred:
function makeCalls() {
return $.ajax(…).then(function() {
return $.ajax(…).done(function() {
// want to execute after second call
});
});
}
makeCalls().done(function() {
// stuff here is now executed after second call as well
});
I have a Javascript object that requires 2 calls out to an external server to build its contents and do anything meaningful. The object is built such that instantiating an instance of it will automatically make these 2 calls. The 2 calls share a common callback function that operates on the returned data and then calls another method. The problem is that the next method should not be called until both methods return. Here is the code as I have implemented it currently:
foo.bar.Object = function() {
this.currentCallbacks = 0;
this.expectedCallbacks = 2;
this.function1 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.function2 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.commonCallback = function(resp) {
this.currentCallbacks++;
// do stuff
if (this.currentCallbacks == this.expectedCallbacks) {
// call new method
}
};
this.function1();
this.function2();
}
As you can see, I am forcing the object to continue after both calls have returned using a simple counter to validate they have both returned. This works but seems like a really poor implementation. I have only worked with Javascript for a few weeks now and am wondering if there is a better method for doing the same thing that I have yet to stumble upon.
Thanks for any and all help.
Unless you're willing to serialize the AJAX there is no other way that I can think of to do what you're proposing. That being said, I think what you have is fairly good, but you might want to clean up the structure a bit to not litter the object you're creating with initialization data.
Here is a function that might help you:
function gate(fn, number_of_calls_before_opening) {
return function() {
arguments.callee._call_count = (arguments.callee._call_count || 0) + 1;
if (arguments.callee._call_count >= number_of_calls_before_opening)
fn.apply(null, arguments);
};
}
This function is what's known as a higher-order function - a function that takes functions as arguments. This particular function returns a function that calls the passed function when it has been called number_of_calls_before_opening times. For example:
var f = gate(function(arg) { alert(arg); }, 2);
f('hello');
f('world'); // An alert will popup for this call.
You could make use of this as your callback method:
foo.bar = function() {
var callback = gate(this.method, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
The second callback, whichever it is will ensure that method is called. But this leads to another problem: the gate function calls the passed function without any context, meaning this will refer to the global object, not the object that you are constructing. There are several ways to get around this: You can either close-over this by aliasing it to me or self. Or you can create another higher order function that does just that.
Here's what the first case would look like:
foo.bar = function() {
var me = this;
var callback = gate(function(a,b,c) { me.method(a,b,c); }, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In the latter case, the other higher order function would be something like the following:
function bind_context(context, fn) {
return function() {
return fn.apply(context, arguments);
};
}
This function returns a function that calls the passed function in the passed context. An example of it would be as follows:
var obj = {};
var func = function(name) { this.name = name; };
var method = bind_context(obj, func);
method('Your Name!');
alert(obj.name); // Your Name!
To put it in perspective, your code would look as follows:
foo.bar = function() {
var callback = gate(bind_context(this, this.method), 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In any case, once you've made these refactorings you will have cleared up the object being constructed of all its members that are only needed for initialization.
I can add that Underscore.js has a nice little helper for this:
Creates a version of the function that will only be run after first
being called count times. Useful for grouping asynchronous responses,
where you want to be sure that all the async calls have finished,
before proceeding.
_.after(count, function)
The code for _after (as-of version 1.5.0):
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
The license info (as-of version 1.5.0)
There is barely another way than to have this counter. Another option would be to use an object {} and add a key for every request and remove it if finished. This way you would know immediately which has returned. But the solution stays the same.
You can change the code a little bit. If it is like in your example that you only need to call another function inside of commonCallback (I called it otherFunction) than you don't need the commonCallback. In order to save the context you did use closures already. Instead of
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
you could do it this way
foo.bar.sendRequest(new RequestObject, function(resp) {
--me.expectedCallbacks || me.otherFunction(resp);
});
That's some good stuff Mr. Kyle.
To put it a bit simpler, I usually use a Start and a Done function.
-The Start function takes a list of functions that will be executed.
-The Done function gets called by the callbacks of your functions that you passed to the start method.
-Additionally, you can pass a function, or list of functions to the done method that will be executed when the last callback completes.
The declarations look like this.
var PendingRequests = 0;
function Start(Requests) {
PendingRequests = Requests.length;
for (var i = 0; i < Requests.length; i++)
Requests[i]();
};
//Called when async responses complete.
function Done(CompletedEvents) {
PendingRequests--;
if (PendingRequests == 0) {
for (var i = 0; i < CompletedEvents.length; i++)
CompletedEvents[i]();
}
}
Here's a simple example using the google maps api.
//Variables
var originAddress = "*Some address/zip code here*"; //Location A
var formattedAddress; //Formatted address of Location B
var distance; //Distance between A and B
var location; //Location B
//This is the start function above. Passing an array of two functions defined below.
Start(new Array(GetPlaceDetails, GetDistances));
//This function makes a request to get detailed information on a place.
//Then callsback with the **GetPlaceDetailsComplete** function
function GetPlaceDetails() {
var request = {
reference: location.reference //Google maps reference id
};
var PlacesService = new google.maps.places.PlacesService(Map);
PlacesService.getDetails(request, GetPlaceDetailsComplete);
}
function GetPlaceDetailsComplete(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
formattedAddress = place.formatted_address;
Done(new Array(PrintDetails));
}
}
function GetDistances() {
distService = new google.maps.DistanceMatrixService();
distService.getDistanceMatrix(
{
origins: originAddress,
destinations: [location.geometry.location], //Location contains lat and lng
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.IMPERIAL,
avoidHighways: false,
avoidTolls: false
}, GetDistancesComplete);
}
function GetDistancesComplete(results, status) {
if (status == google.maps.DistanceMatrixStatus.OK) {
distance = results[0].distance.text;
Done(new Array(PrintDetails));
}
}
function PrintDetails() {
alert(*Whatever you feel like printing.*);
}
So in a nutshell, what we're doing here is
-Passing an array of functions to the Start function
-The Start function calls the functions in the array and sets the number of PendingRequests
-In the callbacks for our pending requests, we call the Done function
-The Done function takes an array of functions
-The Done function decrements the PendingRequests counter
-If their are no more pending requests, we call the functions passed to the Done function
That's a simple, but practicle example of sychronizing web calls. I tried to use an example of something that's widely used, so I went with the Google maps api. I hope someone finds this useful.
Another way would be to have a sync point thanks to a timer. It is not beautiful, but it has the advantage of not having to add the call to the next function inside the callback.
Here the function execute_jobs is the entry point. it take a list of data to execute simultaneously. It first sets the number of jobs to wait to the size of the list. Then it set a timer to test for the end condition (the number falling down to 0). And finally it sends a job for each data. Each job decrease the number of awaited jobs by one.
It would look like something like that:
var g_numJobs = 0;
function async_task(data) {
//
// ... execute the task on the data ...
//
// Decrease the number of jobs left to execute.
--g_numJobs;
}
function execute_jobs(list) {
// Set the number of jobs we want to wait for.
g_numJobs = list.length;
// Set the timer (test every 50ms).
var timer = setInterval(function() {
if(g_numJobs == 0) {
clearInterval(timer);
do_next_action();
}
}, 50);
// Send the jobs.
for(var i = 0; i < list.length; ++i) {
async_task(list[i]));
}
}
To improve this code you can do a Job and JobList classes. The Job would execute a callback and decrease the number of pending jobs, while the JobList would aggregate the timer and call the callback to the next action once the jobs are finished.
I shared the same frustration. As I chained more asynchronous calls, it became a callback hell. So, I came up with my own solution. I'm sure there are similar solutions out there, but I wanted to create something very simple and easy to use. Asynq is a script that I wrote to chain asynchronous tasks. So to run f2 after f1, you can do:
asynq.run(f1, f2)
You can chain as many functions as you want. You can also specify parameters or run a series of tasks on elements in an array too. I hope this library can solve your issues or similar issues others are having.