I want function A to finish execution and only after that function B should start executing. When I call function A and then function B, it seems both are executing simultaneously. And after function B completes, I want to call a third function update_dropdown().
My code looks like this:
function A {
for (var i = 0; i < 5; i++) {
var promise = $.get(url+i);
$.when(promise).then(function () {
$.post(url);
});
}
}
function B {
var x = $.get(url);
var promise = $.post(url+x);
$.when(promise0).then(function () {
update_dropdown();
});
}
Please can you tell me how I can make these 3 function calls happen sequentially.
OK, it's getting a little bit clearer what you actually want (based on your recent comments to address clarifying questions) though there are still at least two options open.
For an operation like this, you probably want to take advantage of a number of promise features:
jQuery's Ajax calls already return a promise so you can just use those directly
To serialize operations, you can just chain multiple promise operations together
To make async operations serialize properly, you can return a promise from a .then() handler and the master promise will resolve only when all the chained promises have resolved (kind of a built-in $.when() without having to explicitly call $.when()).
You can chain as many operations together as you want and the master promise will tell you when they are all done.
If you return promises from both A() and B(), then the callers of those functions can monitor when they are done with promise methods which then lets you chain A().then(B) to sequence those two.
When you sequence operations with chaining, the prior methods resolve data is passed to the next .then() handler function in the chain as the first argument to the .then() handler function so if you need the prior data for the next operation, it is right there to use.
So, with all those capabilities, it's just a matter of putting the right scaffolding around the code to implement the exact sequencing you want. Here are two different options:
Option 1: If you want to serialize everything in A() so that all 10 requests happen in serial fashion (the next one proceeds only when the prior one is done), then it could look like this:
// serialize all requests
function A() {
var p = $.get(url).then(function(data) {return $.post(url)});
for (var i = 1; i < 5; i++) {
// chain four more pairs of requests onto the original promise
p = p.then(function() {return $.get(url)})
.then(function(data) {return $.post(url)});
}
// return the promise so callers can monitor when A() is done
return p;
}
function B() {
// sequence these three operations one after the other
return ($.get(url)
.then(function(data) {return $.post(url + x)})
.then(update_dropdown)
);
}
// run them both, one after the other
A().then(B);
Option 2: If you want the 5 pairs of requests in A() to run in parallel, with only the last part of A() waiting until the 5 pairs of requests are done, then it could look like this:
// parallelize pairs of requests
function A() {
var promises = [];
for (var i = 0; i < 5; i++) {
// execute 5 pairs of requests where each pair is serialized in itself
promises.push($.get(url).then(function(data) {return $.post(url)}));
}
// return a promise that resolves only when all the other promises are done
return $.when.apply($, promises);
}
function B() {
// sequence these three operations one after the other
return ($.get(url)
.then(function(data) {return $.post(url + x)})
.then(update_dropdown)
);
}
// run them both, one after the other
A().then(B);
These use the concept that if you return a promise from a .then() handler function, then it will chain multiple async operations together and the master promise is only resolved when all the chained operations are resolved. This is very powerful for sequencing multiple ajax operations and you can even do it for operations in a loop like you have.
Something like this should work
function A {
var xhr = [];
for (var i = 0; i < 5; i++) {
xhr.push( $.get(url) );
}
$.when.apply($, xhr).then(B);
}
function B {
$.get(url).done(function(x) {
$.post(url + x).done(update_dropdown);
});
}
Note the use of an array to keep the promises in, then using $.when with apply() to fire a callback when all the ajax requests in the loop has finished.
Assumptions assumptions ...
Let's assume that :
the url for every get is the same as that for its corresponding post
the urls for each get-post pair should vary
the five get-post pairs in A can occur in parallel and we are not interested in the returned data
First, a utility function :
function getThenPost(url, appendToURL) {
return $.get(url).then(function(x) {
return (appendToURL) ? $.post(url + x) : $.post(url);
});
}
then A and B, both of which call the utility :
function A(urls) {
return $.when.apply(null, urls.map(function(url) {
return getThenPost(url, false);
}));
}
function B(url) {
return getThenPost(url, true);
}
and finally an expression that calls A and B :
A(['/path/0', '/path/1', '/path/2', '/path/3', '/path/4']).then(function() {
B('/path/5');
}).then(update_dropdown);
It should be reasonably simple to adjust this code if assumptions 1 and 2 are incorrect.
If assumption 3 is incorrect then A will require more extensive modification.
We can call our choice function in our way using jquery Deferred Object.
It is very simple let see successfully run example:
<body>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
// I want to call function in order of f1,f2,f3,f4 every time when i will execute this html page.
promise = f1().then(f2).then(f3).then(f4); // Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.
function f1() {
var d = $.Deferred();
setTimeout(function() {
// our code here....
alert("1");
console.log("1");
d.resolve(); // resolve() :Resolve a Deferred object and call any doneCallbacks with the given args.
},1000); // You set some time for each method.
return d.promise(); //promise(): Return a Deferred’s Promise object.
}
function f2() {
var d = $.Deferred();
setTimeout(function() {
alert("2");
console.log("2");
d.resolve();
},1000);
return d.promise();
}
function f4() {
var d = $.Deferred();
setTimeout(function() {
alert("4");
console.log("4");
d.resolve();
},1000);
return d.promise();
}
function f3() {
var d = $.Deferred();
setTimeout(function() {
alert("3");
console.log("3");
d.resolve();
},1000);
return d.promise();
}
</script>
Javascript without extra work is single threaded. that means functions are not able to be executed simultaneously. but the problem is that the $.get() and $.post() calls are asynchronous. that means they are executed whenever the requested data arrives your client. (first come first serve)
an solution would be to execute function B after all the results ob A arrived, or to hold back all results and handle all data at once then run update_dropdown().
Related
I am dealing with an old codebase and faced this situation where it's difficult for me to understand the order of execution after promises are resolved. I am more familiar with async/await syntax or with a chain of then-s, but not with this one. Here is the snippet:
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises
My question is: What would be the order in this case for all then-s that come after these promises?
Is it exactly sequential as it is or it's not and we can refactor it with say Promise.all?
Does it even need any refactoring?
What would be the order in this case for all then-s that come after these promises?
The then function will fire when the associated promise resolves. That's the point of asynchronous code. It goes away until it is ready to do the next thing.
we can refactor it with say Promise.all
You could use Promise.all to wait until multiple promises are resolved before doing anything with the resulting values, but that would be inefficient unless the then function for C requires data from A or B.
loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises they will get inserted into Event loop one after another.
then part of promises will get executed once promises are resolved. Which promise's then will get executed depends upon the execution of the respective promise.
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
Promise.all is like a gate where you can pass array of promises and it will resolve only after all the promises are resolved.
let p1 = Promise.resolve(loadLatestDatasetVersion(oDataModel));
let p2 = Promise.resolve(loadCountries(oDataModel));
let p3 = Promise.resolve(numberOfRulesetChanges(oDataModel));
let p4 = Promise.resolve( fireImplicitLock(caseUuid)
let finalPromise = Promise.all([p1,p2,p3,p4]);
finalPromise.then(([datasetVersion,countries,elements,lockData])=>{
// do something here with datasetVersion, countries, elements, lockData you have all the params cause all the promises are resolved.
})
Same thing you can achieve using Promise.all like shown above.
More about promises
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
Above line will not return any result from promises, it will return Promise object with its status resolved/pending/rejected and value.
In testing I've found that JavaScript Promises are always asynchronous regardless of whether or not they contain any asynchronous functions in their chain.
Here is some code that shows the order of operations in console. If you run it you will see that even though every function is synchronous the output shows both of the aPromise() calls being run in parallel, and "surprisingly this happens after run 2 finishes" not happening before run 2 finishes.
function aPromise() {
return new Promise(function(resolve, reject) {
console.log("making promise A")
resolve(bPromise());
console.log("promise A resolved")
});
}
function bPromise() {
return new Promise(function(resolve, reject) {
console.log("making and resolving promise B")
resolve();
});
}
aPromise().then(function() {
console.log("finish run 1");
}).then(function() {
console.log("surprisingly this happens after run 2 finishes");
});
aPromise().then(function() {
console.log("finish run 2");
})
Output to console:
making promise A
making and resolving promise B
promise A resolved
making promise A
making and resolving promise B
promise A resolved
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
So, Why are JavaScript promises asynchronous when calling only synchronous functions? What is happening behind the scenes that leads to this behavior?
P.S. In order to better understand this I implemented my own Promise system and I found that making synchronous functions happen in the expected order was easy but making them happen in parallel was something I could only accomplish by putting a setTimeout() of a few milliseconds at every resolve (My guess is that this is not what's happening with vanilla promises and that they are actually being multi threaded).
This has been a small problem for one of my programs where I'm traversing a tree applying an array of functions to each node and putting the functions in queue if that node has an asynchronous function already running. Most of the functions are synchronous so the queue is rarely used but upon switching over from callbacks (hell) to Promises I've been having an issue where the queues get used almost always as a result of Promises never running synchronously. It's not a huge problem but it is a bit of a debugging nightmare.
1 Year Later EDIT
I ended up writing some code to deal with this. It's not amazingly thorough, but I've used it with success to solve the issue I was having.
var SyncPromise = function(fn) {
var syncable = this;
syncable.state = "pending";
syncable.value;
var wrappedFn = function(resolve, reject) {
var fakeResolve = function(val) {
syncable.value = val;
syncable.state = "fulfilled";
resolve(val);
}
fn(fakeResolve, reject);
}
var out = new Promise(wrappedFn);
out.syncable = syncable;
return out;
}
SyncPromise.resolved = function(result) {
return new SyncPromise(function(resolve) { resolve(result); });
}
SyncPromise.all = function(promises) {
for(var i = 0; i < promises.length; i++) {
if(promises[i].syncable && promises[i].syncable.state == "fulfilled") {
promises.splice(i, 1);
i--;
}
// else console.log("syncable not fulfilled" + promises[i].syncable.state)
}
if(promises.length == 0)
return SyncPromise.resolved();
else
return new SyncPromise(function(resolve) { Promise.all(promises).then(resolve); });
}
Promise.prototype.syncThen = function (nextFn) {
if(this.syncable && this.syncable.state == "fulfilled") {
//
if(nextFn instanceof Promise) {
return nextFn;
}
else if(typeof nextFn == "function") {
var val = this.syncable.value;
var out = nextFn(val);
return new SyncPromise(function(resolve) { resolve(out); });
}
else {
PINE.err("nextFn is not a function or promise", nextFn);
}
}
else {
// console.log("default promise");
return this.then(nextFn);
}
}
The callback passed to a Promise constructor is always called synchronously, but the callbacks passed into then are always called asynchronously (you could use setTimeout with a delay of 0 in a userland implementation to achieve that).
Simplifying your example (and giving the anonymous function's names so I can refer to them) to:
Promise.resolve().then(function callbackA () {
console.log("finish run 1");
}).then(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve().then(function callbackC () {
console.log("finish run 2");
})
Still gives the output in the same order:
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
Events happen in this order:
The first promise is resolved (synchronously)
callbackA is added to the event loop's queue
The second promise is resolved
callbackC is added to the event loop's queue
There is nothing left to do so the event loop is accessed, callbackA is first in the queue so it is executed, it doesn't return a promise so the intermediate promise for callbackB is immediately resolved synchronously, which appends callbackB to the event loop's queue.
There is nothing left to do so the event loop is accessed, callbackC is first in the queue so it is executed.
There is nothing left to do so the event loop is accessed, callbackB is first in the queue so it is executed.
The easiest way I can think of to work around your problem is to use a library that has an Promise.prototype.isFulfilled function you can use to decide whether to call your second callback synchronously or not. For example:
var Promise = require( 'bluebird' );
Promise.prototype._SEPH_syncThen = function ( callback ) {
return (
this.isPending()
? this.then( callback )
: Promise.resolve( callback( this.value() ) )
);
}
Promise.resolve()._SEPH_syncThen(function callbackA () {
console.log("finish run 1");
})._SEPH_syncThen(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve()._SEPH_syncThen(function callbackC () {
console.log("finish run 2");
})
This outputs:
finish run 1
surprisingly this happens after run 2 finishes
finish run 2
Your code is fine is you want your promises to run independently and let them execute in their own way, no matter each one complete first. As soon as your code is async, you cannot predict which one will be completed first (due to the async nature of the event loop).
However if you want to catch all your promises after they all completed, you should use Promise.all (which is the equivalent of $.when is jQuery).
See:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
The closest answer I could find was this https://stackoverflow.com/a/17216555/2834734
The most common use for .then is chaining ajax requests:
$.ajax({...}).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); });
this can easily be done in a loop
However it's the looping procedure I'm having difficulty with plus I have some unusual circumstances.
A brief explanation is, I have an array of requests that I need to loop through, some will invoke an ajax load and others will not. I need them to run consecutively but also run a specific function call at then end.
Here is a simple(I hope) sample of my situation:
// Here is my flow of logic
var thingsToDo = new tasks(); // Initiate the constructor, explained below
// Loop through the requests array and process them consecutively
for (var i in thingsToDo.requests) {
$.when(thingsToDo.jqxhr).then(function() {
thingsToDo.requests[i].fn();
})
}
// Run my final function at the end of the chain.
$.when(thingsToDo.jqxhr).then(function() {
runOnceAllComplete();
});
This is the constructor class the above is based on.
// Constructor
function tasks() {
_tasks_ = this; // automatic global var
this.jqxhr = undefined; // Var to monitor ajax calls
this.requests = [ // list of tasks to run.
{
url: 'file1.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
}, {
url: 'file2.php',
fn: function() {
_tasks_.load(this.url);
console.log('file2 loaded');
}
}, {
noUrl: true, // Note there is no file to load here
fn: function() {
console.log('no file here to load, but process something else');
$('body').css("background-color", "blue");
}
}, {
url: 'file3.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
},
];
this.load = function(file) { // This runs the ajax call and resets this.jqxhr
this.jqxhr = $.get(file);
}
}
function runOnceAllComplete() {
alert('hooray!, we finished');
}
The tricky part I have is the requests are created dynamically so there can be 1-n many requests to perform, which is why I chose to loop, and they must be performed in that order.
As mentioned some requests will invoke an ajax call and others may not, this doesn't seem to break $.when().then(), but the problem is the loop continues before the promise is resolved and my final function happens before the final request.
Still trying to get my head around promises, the first time I've used them.
Try including return statement at fn , this.load ; adding .promise() chained to $("body") at fn to return a jQuery promise object ; using Function.prototype.apply() , $.map() at $.when()
fn: function() {
// added `return`
return _tasks_.load(this.url);
}
this.load = function(file) {
this.jqxhr = $.get(file);
// added `return`
return this.jqxhr
}
fn: function() {
console.log('no file here to load, but process something else');
// added `return` , `.promise()`
return $('body').css("background-color", "blue").promise();
}
$.when.apply($, $.map(thingsToDo.requests, function(task) {
return task.fn()
})).then(runOnceAllComplete)
See also Pass in an array of Deferreds to $.when() , What does $.when.apply($, someArray) do?
however I'm encountering a problem, using the .map() it doesn't wait
for each request to complete before processing the next one. I need
each one to complete before moving to the next.
Try using .queue() , which will calls functions in queue sequentially, and only when next is called at current function
$(thingsToDo).queue("tasks", $.map(thingsToDo.requests, function(task) {
return function(next) {
// call next function in `"tasks"` queue
// when current function completes using `.then(next)`
return task.fn().then(next)
}
})).dequeue("tasks").promise("tasks").then(runOnceAllComplete)
See .queue() , .promise() , Execute function queue in javascript
I have this code as a starting point.
// $ = jQuery
// groupAdata and groupBdata are arrays
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// this is an example on how this function calls other functions asynchronously.
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupAdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
function endofitall() {
// call this after all instances of funcA and funcB are done.
}
When running endofitall(), I'd like to be sure that all calls of funcA and funcB are done.
I take that Promises and jQuery.Deferred() would be a good/preferred approach but was not able to map the answers I found to this specific scenario. (It is part of a templating tool that fires multiple dom manipulators func[AB] for multiple DOM elements.)
You can use $.when().
Your goal should be to get to:
// call funcA, call funcB
$.when( funcA(), funcB() )
// when everything is done go on with the callback
.done(endofitall);
In the case of funcA (synchronous function there's no problem and it will work as is).
In the case of funcB (asynchronous) there are some things to consider. If it would be just one ajax call your code should be something like:
// This function returns a promise.
// When it's fulfilled the callback (in your case '.done(endofitall)')
// will be called.
function funcB(somedata){
return $.post(url, somedata);
}
As you are actually making more requests you have to return a resolved promise only when all calls have been fulfilled.
// an *Asynchronous* function, returns an array of promises
function funcB(elem, groupAdata) {
var allCalls = [];
// for each element in the array call the relative async
// function. While you're calling it push it to the array.
groupAdata.forEach(data, function(data){
allCalls.push( $.post(url, data) );
});
// allCalls is now an array of promises.
// why .apply(undefined)? read here: https://stackoverflow.com/a/14352218/1446845
return $.when.apply(undefined, allCalls);
}
At this point you can go for a flat and clear:
$.when( funcA(), funcB() ).done(endofitall);
As a rule of thumb: if you are making async requests try to always return a promise from them, this will help flatten out your code (will post some link later on if you want) and to leverage the power of callbacks.
The above code can surely be refactored further (also, I haven't used a lot of jQuery in the last few years, but the concept applies to any Js library or even when using no library at all) but I hope it will help as a starting point.
References:
$.when
A similar answer here on SO
Call endofitall() inside each iteration for funcA and funcB. Keep a counter and perform the actual work once the counter reaches the number signifying all the tasks are complete.
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// these calls are not async
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
endofitall();
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupBdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
endofitall();
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
var counter=0;
function endofitall() {
if(++counter==groupAdata.length + groupBdata.length){
//do stuff
}
I'm having some confusion about how to apply the jQuery $.deferred object for a loop of asynchronous process when the loop is of variable length. Below is my current attempt. Because the process in the loop are asynchronous the loop will complete and the deferred object in main will resolve before all the asynchronous process triggered in the loop have resolved. On the other hand I will be getting a variable number of resolutions triggered from the deferred object inside the asynchronous process as each one finishes, leaving quite a bit of accounting to determine when they have all completed.
function asyncProcess(someVariable) {
var dfd = $.Deferred();
//Some Code
dfd.resolve();
return dfd.promise();
}
function main() {
var dfd = $.Deferred();
for (i = 0; i < variableInteger; i++) {
asyncProcess(someVariable);
dfd.resolve();
}
return dfd.promise();
}
Does anyone know how this situation is typically handled?
Use $.when() to create a new promise which will be resolved when all the promises are resolved or any one is rejected.
function asyncProcess(someVariable) {
var dfd = $.Deferred();
//Some Code
dfd.resolve();
return dfd.promise();
}
function main() {
var prmis = [];
for (i = 0; i < variableInteger; i++) {
prmis.push(asyncProcess(someVariable));
}
return $.when.apply($, prmis);
}