I am having a hard time understanding the value of creating your own Deferred object.
Say you have the following jQuery
function doStuffFirst() {
var dfd = $.Deferred();
// do some stuff here
setTimeout(function() {
dfd.resolve();
},1);
return dfd.promise();
}
function doStuffAfter() {
//do some other stuff
}
$.when(doStuffFirst()).done(doStuffAfter);
I don't actually know that doStuffFirst() has finished, its just waiting some time before firing doStuffAfter()
why is that any better than just going
function doStuffFirst() {
// do some stuff here
}
setTimeout(function() {
//do some other stuff
},1);
You do know that it is finished; but it is useless as it stands, since you're not executing your task asynchronously. The task executes, then the deferred gets created and fired almost immediately. However, if you change to this:
function doStuffFirst() {
var dfd = $.Deferred();
setTimeout(function() {
// do some stuff HERE
dfd.resolve();
},1);
return dfd.promise();
}
then it becomes useful, since it will return immediately, but resolve some time later (whenever the task is done). While you have just one async task, using a deferred is not much different than using a plain callback (just more complex, and prettier, and the dependency goes the other way: callbacks go in, while promises come out of the routine that schedules the task). However, if you have more complex requirements, like having two async tasks which you want to execute simultaneously but wait until both are done, promises are much superior.
Using setTimeout simply executes the code after the given time, whereas promises executes code once the promised task has completed. Promises are used when dealing with things in which completion time is unknown, such as ajax requests. In setTimeout, you know when exactly to execute some code.
Related
I'm having trouble understanding how Node operates regarding it's parallel processing and returning values from function calls.
FYI: The gulp function below is merely created as an example for this question.
Is it possible that the function could return the stream before the Read a large file statement has finished processing (the large file has been fully read from the file system and the stream has been added), or is Node smart enough to complete all statements before returning?
function moveFiles(){
var gulp = require('gulp'),
stream = require('merge-stream')();
// Read a large file
stream.add(gulp.src('src/large-file.txt')
.pipe(gulp.dest('dest/'))
);
// Read a small file
stream.add(gulp.src('src/small-file.txt')
.pipe(gulp.dest('dest/'))
);
return (stream.isEmpty() ? null : stream);
}
Could Node feasibly return a value from a function call before completing all operations within the function itself?
This is a tricky question. The answer is no, in a way that returning a value means that the function is finished executing, it's taken back from the stack and it will never do anything again - unless it's invoked another time of course, but the point is that this particular invocation is over.
But the tricky part is that it's the function that's finished executing and it doesn't mean that it couldn't schedule something else to happen in the future. It will get more complicated in a minute but first a very simple example.
function x() {
setTimeout(function () {
console.log('x1'));
}, 2000);
console.log('x2');
return;
console.log('x3');
}
Here when you call x() then it will schedule another function to run after 2 seconds, then it will print x2 and then it will return - at which point this function cannot do anything else ever again for that invocation.
It means that x3 will never get printed, but x1 will eventually get printed - because it's another function that will be called when the timeout fires. The anonymous function will get called not because the x() function can do anything after it returns, but because it managed to schedule the timeout before it returned.
Now, instead of just scheduling things to happen in the future, a function can return a promise that will get resolved some time later. For example:
function y() {
console.log('y1');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('message from y()');
}, 2000);
});
console.log('y2');
}
Now, when you run:
var promise = y();
what will happen is that y1 will get printed, a new promise will get returned and y2 will never get printed because at that point y() returned and cannot do anything else. But it managed to schedule a timeout that will resolve the promise after two seconds.
You can observe it with:
promise.then(function (value) {
console.log(value);
});
So with this example you can see that while the y() function itself returned and cannot do anything else, some other (anonymous in this case) function can be called in the future and finish the job that the y() function has initiated.
So I hope now it's clear why it's a tricky question. In a way a function cannot do anything after returning. But it could have scheduled some other functions as timeouts, event handlers etc. that can do something after the functions returns. And if the thing that the function returns is a promise then the caller can easily observe the value in the future when it's ready.
All of the examples could be simplified by using the arrow functions but I wanted to make it explicit that those are all separate functions, some of them are named, some are anonymous.
For more details see some of those answers:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation of callbacks, promises and how to access data returned asynchronously
I have a function like this
function queuingFunction(name){
var deferred = $q.defer()
$timeout(function(){
console.log("my name is ",name);
deferred.resolve(true);
},3000)
return deferred.promise;
}
i am calling the function like this
communicatorBaseService.queuingFunction("Jack");
communicatorBaseService.queuingFunction("max");
communicatorBaseService.queuingFunction("Ray");
In the console all the 3 results are displayed after 3 second.
What i need is , at the 3rd second Jack shows in console, after again 3 seconds max is shown then again after 3 second Ray is shown.
If i call queuingFunction in between the execution it should get added to a execution queue.
What i was thinking to do was
Receive the request to execute the function
Add the params to a queue
Call the queuingFunction with the queue
Run the queuingFunction with the queue[0]
On complete of queuingFunction delete the queue[0]
Check if anything is left at position queue[0] re-run the function with the current queue.
Pretty sure this is not the best way, what can be a good way to do this. I am using Angular thus $q is there in code. I don't want to use jQuery.
It sounds from your comments like you want to be able to just make some function call and have it's operation automatically sequenced with the ones that came before. If so, you will need some sort of queue object that can accumulate the operations underway and pending.
Promises already are a sort of queue, so if you can start with a promise and just add a new promise onto the end of the prior operation each time you make your function call, it can sequence them. Here's one way to do that:
// initialize queue with a resolved promise
var queue = $q();
function queuingFunction(name){
// chain this operation onto whatever operation was previously queued
queue = queue.then(function() {
var deferred = $q.defer()
$timeout(function(){
console.log("my name is ",name);
deferred.resolve(true);
},3000)
return deferred.promise;
});
}
Then, you can just call:
queuingFunction("Jack");
queuingFunction("max");
queuingFunction("Ray");
And, the three operations will be sequential.
Here's a working demo using ES6 standard promises: http://jsfiddle.net/jfriend00/adq3L6zt/
Using #jfriend00 approach I did the following using angular $q if anyone interested
var queue = $q(function(resolve,reject){resolve()});
service.queuingFunction = function(name){
// perform some asynchronous operation, resolve or reject the promise when appropriate.
queue = queue.then(function() {
var deferred = $q.defer()
$timeout(function(){
console.log("my name is ",name);
deferred.resolve(true);
},3000)
return deferred.promise;
});
}
I have situation where I believe I need to create a Deferred object with a "then" handler, but wait until the "then" handler has completed it's own promise before moving on.
The use case is a record object, and the above function is it's save method. The record object has an attribute called saveQueue, which is set to $.Deferred() on the record's instantiation. The resolve call on saveQueue was supposed to make sure the Deferred there is always executing every new handler attached to it as soon as it could. The idea being that you can call save several times on the record in short succession, but the calls will run one after another, and not overlap.
I am using a Deferred to enqueue Ajax calls, so that one does not run until the previous one call finished. However, from the same method, I want to return a Deferred that can be resolved/rejected by the jQuery Ajax object, like so:
record.saveQueue = $.Deferred();
self.save = function( record ){
var deferredAction = $.Deferred();
deferredAction.then(function() {
return $.post("/example_save_endpoint");
});
record.saveQueue.always(function(){
deferredAction.resolve();
}).resolve();
return deferredAction;
}
However, when I use this code, the deferredAction promise always ends up resolved, presumably because the #then handler is returning a "pending" (and thus non-rejecting) promise. Is there any way to force the Deferred to wait until the Ajax promise is complete before resolving/rejecting? Or is there another, better way to thread this needle?
Your idea might work, but
the queue must not be resolved using .resolve() every time the method is called, instead it should be initialised only with a resolved promise.
to actually queue on the record.saveQueue, it needs to be changed (overwritten) on every method call, to represent the end of the latest request.
And we don't need any deferreds for that, as we can work with the promises that $.post returns.
So use this:
var emptyQueue = $.when(undefined); // an already fulfilled promise as the start
// equivalent: = $.Deferred().resolve().promise();
function startQueue() {
return emptyQueue; // yes, this delibaretely returns a constant, the begin
// of the queue always looks the same (and is never mutated)
}
// every time you create a record, do
record.saveQueue = startQueue();
// and use that in your methods:
this.save = function(record) {
var queuedRequestResult = record.saveQueue.then(function() {
return $.post("/example_save_endpoint");
// ^^^^^^ promises chain :-)
});
// Magic happens here:
record.saveQueue = queuedRequestResult // we swap the previous queue promise for a new
// one that resolves only after the request
.then(startQueue, startQueue); // and make sure it then starts with a fresh
// queue, especially when the request failed
//.then(null, startQueue) is similar, except unnecessarily remembering the last result
return queuedRequestResult;
}
I would probably choose not to do it this way, but a deferred/promise can indeed be used as a queuing device.
You need a slight(?) variation of what you already tried.
self.queue = $.when();//A resolved promise, used to form a queue of functions in a .then() chain.
self.save = function(data) {
var dfrd = $.Deferred();//A Deferred dedicated to this particular save.
self.queue = self.queue.then(function() {
return $.post("/example_save_endpoint", data) //Make the AJAX call, and return a jqXHR to ensure the downstream queue waits for this jqXHR to resolve/reject.
.then(dfrd.resolve, dfrd.reject) //Resolve/reject the Deferred for the caller's benefit
.then(null, function() {
//Force failure down the success path to ensure the queue is not killed by an AJAX failure.
return $.when();//Return a resolved promsie, for the queue's benefit.
});
});
return dfrd.promise();//allow the caller to do something when the AJAX eventually responds
}
For explanation, see comments in code
I am working with a framework that uses jQuery Deferred Objects.
In my code, one async operation should executed successfully so that another async operation be executed.
function doOperation() {
var def2mock = $.Deferred();
var def1 = doSomeAsyncOperation().done(function () {
var def2 = doAnotherAsyncOperation();
});
return $.when(def1, def2mock);
}
So I put the call for the second operation in the "done" promise of the deferred of the first operation as in the example.
Now, I wrap this sequence with the function doOperation. The result of doOperation should be the join of the two deferreds of both async calls. Meaning doOperation succeeds if all async operations succeed and fails if any of them fail, which is exactly what $.when does.
The problem is that in order to create the join with $.when, I need both deferreds present at the time $.when is called. And because the deferred of the second async operation is not available at that moment, I had to find a way to create the join first, then add the deferred of the second async operation to the join later.
To do this, I thought I can define a new mock $.Deferred named def2 in doOperation. And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one. The only way I found was to do
def2.done(def2mock.resolve)
// and
def2.fail(def2mock.reject)
but I don't feel this manual linking is the right way to do it.
So please tell me if you have a better suggestion on how to do it the right way.
And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one.
This is actually what .then does: It returns a new promise which gets resolved/rejected when the promise returned from the passed callback is resolved/rejected.
In order to make this work with $.when, you have to keep a reference to the original promise:
function doOperation() {
var def1 = doSomeAsyncOperation();
var def2 = def1.then(function () {
return doAnotherAsyncOperation();
});
return $.when(def1, def2);
}
DEMO
def2 will be resolved when the promise returned by the callback is resolved, i.e. the one that doAnotherAsyncOperation returns.
But I don't think using $.when is a good choice here, from a conceptional point of view. You are actually executing two asynchronous functions sequentially, but $.when is used to run asynchronous functions in parallel.
I would just chain the function calls via .then and collect the responses:
function doOperation() {
return doSomeAsyncOperation().then(function (resp1) {
return doAnotherAsyncOperation().then(function(resp2) {
return [resp1, resp2];
});
});
}
DEMO
I'd say it's clearer here that doAnotherAsyncOperation is executed after doSomeAsyncOperation and the result of both calls is available to the caller of doOperation.
You might have to do something similar for the fail case.
I don't have any experience with Deferred (nor do I fully understand what it does), but if you are just looking to chain operations, would something like this work?
function doOperation(operations) {
var wrapper = {};
wrapper.index = 0;
wrapper.operations = operations;
wrapper.onthen = $.proxy(function() {
if (this.index < this.operations.length) {
this.operations[this.index]().then(this.onthen);
}
this.index++;
}, wrapper);
wrapper.onthen();
}
Here's a demonstration: http://jsfiddle.net/vz6D7/2/
I have a set of async functions that issue executeSql commands to drop 2 tables, and have callbacks to create the 2 tables and populate the 2 tables.
I'd like to know when they have ALL completed.
I wouldn't mind if they executed synchronously. In fact, I'd prefer that they ran synchronously!
Q: Would I use the jQuery pipe method to queue up these functions so that they execute in a more traditional way than issuing callbacks?
I want to do something like:
DropTableA();
CreateTableA();
PopulateTableA();
DropTableB();
CreateTableB();
PopulateTableB();
window.location.replace('Index.htm');
Have a look at jQuery's Deferreds and Promises.
In short, here's an example of an async function that executes something asynchronously (setTimeout in this case). When calling the function you get back a promise.
var myFunc = function (value) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(42 * value);
}, 1000);
return d.promise();
};
var promise = myFunc(100);
promise.done(function (res) { console.log(res); });
You can use $.when to execute something when all promieses have been fulfilled, i.e.
$.when(p1, p2, p3).then(...)
If you want to execute them in order you can chain the deferreds using the deffered's pipe.
If they are all sinchronous function (no async call inside those function) you are assured that window.location.replace('Index.htm'); is called after all of them. If you make ajax call you could do
jQuery.ajaxSetup( {async: false});
befare calling the first function and you are ok because all your AJAX call are now synchronous
http://jsfiddle.net/ywL63/
This is where jQuery deferred/promise come into play. Basically you modify your functions like this
function myAction1() {
var dfd = $.Deferred();
$(selector).whatever(function() { dfd.resolve(); // this is one of the callbacks});
return dfd.promise();
}
Then you can go and "wait" for all of the actions to complete with a construct like
$.when(myAction1(), myAction2(), ...).then(function() {
// this will be executed when all the actions finished = resolved
}
If your functions are synchronous, just call them in the right order, as you did in your example.
Assuming instead that your function returns a promise() or an observable object you can simply do
$.when(fn1(), fn2(), ... ).done(function() {
/* here you know that all functions have been returned, as you asked */
})
(note that you can use deferred objects not only for asynchronous tasks)
Edit: if order matters you can use this plugin:
jQuery.whenSync() Plugin For Chaining Asynchronous Callbacks Using Deferred Objects