$.when.apply not evaluating any of defined functions in array - javascript

I can't get $.when.apply to evaluate any of the defined functions in array, what am I doing wrong here?
function Logic(address) {
this.address = address;
}
Logic.prototype.Get = function (pk, success, failure) {
var scope = this;
return $.ajax({
url: scope.address + '/Get',
type: "GET",
data: { 'pk': pk },
contentType: "application/json; charset=utf-8",
success: function (data) {
success(data.hasOwnProperty("d") ? data.d : data);
},
failure: function (ex) {
failure(ex);
}
});
};
function Screen(options) {
var scope = this;
if (options.pullings != null)
{
$.each(options.pullings , function (i, pulling)
{
scope.pullings.push(function () {
return pulling.logic.Get($('[name="' + pulling.pkField + '"]').val(),
function (row) {
$('#' + pulling.displayControlID).val(row[pulling.displayField]);
}, null);
});
});
}
}
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
});
}

Because $.when() takes Promises or plain values. The function objects you pass in are considered to be values. Why did you expect them to be invoked automatically? You have to do that manually:
$.when.apply($, $.map(scope.pullings, function(fn) {
// calls every function
return fn();
})).then(function() {
// this block gets called when all results are available
});

Looks like a syntax error, change:
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
}
}
to:
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
});
}
That's my initial thinking, have you checked the console to see what errors you might be getting?

An overlooked alternative to $.when.apply is to accumulate when promises in the loop e.g. using the pattern promise = $.when(promise, anotherpromise)
e.g. something like
// Start with a resolved promise - which undefined represents!
var promise;
$.each(options.pullings, function (i, pulling) {
promise = $.when(promise, Get($('[name="' + pulling.pkField + '"]').val(),
function (row) {
$('#' + pulling.displayControlID).val(row[pulling.displayField]);
}, null);
});
promise.then(function(){
// called when all promises are completed
});

Related

I need a way to execute an array of ajax calls wrapped in a function

The system I'm working with was designed to only make synchronous ajax calls, so i am looking for a workaround. First i have an ajax call that is wrapped in a function. I then wrap it in another function so it doesn't get executed when adding it to the array. So i have two arrays of async ajax call functions. I would like to execute everything in the first array, and then wait until everything has completed. I would then like to execute everything in a second array. This is what i have so far
I have a loop that goes through items and I have a wrap function for each item that takes in my already wrapped ajax call so that it doesn't get executed and stores it in an array like below
var postpromises = [];
var WrapFunction = function (fn, context, params) {
return function () {
fn.apply(context, params);
};
}
var postPromise = WrapFunction(ajaxFunction, this, [{
url: url,
data: j,
async: true,
type: 'POST',
success: function (data) {
//success
},
error: function (xhr, textStatus, errorThrown) {
//error
}
}]);
postpromises.push(postPromise);
I then have the same code for validation. So before I move on to next page, I have the following
$.when.apply(undefined, postpromises).then(function () {
console.log();
$.when.apply(undefined, validatepromises).then(function () {
console.log();
});
});
The issue is that when I get to the code above, none of my postpromises even get executed, so I feel like I may be missing something here.
Ideas?
The function $.when require a promise, in your code you are returning a function that return nothing, so just return the result of the wrapped function:
ES6 spread operator REF
function arguments object REF
var postpromises = [];
var validatepromises = [];
function f1() {
var fakePromise = $.Deferred();
setTimeout(() => {
fakePromise.resolve("IM RESOLVED!!");
}, 500);
return fakePromise.promise();
}
//OLD ONE
/*var WrapFunction = function (fn, context, params) {
return function () {
fn.apply(context, params);
};
}*/
var WrapFunction = function(fn, context, params) {
return function() {
return fn.apply(context, params);
}();
}
var postPromise = WrapFunction(f1, this, []);
postpromises = [postPromise];
var validatePromise = WrapFunction(f1, this, []);
validatepromises = [validatePromise];
//OLD ONE
/*$.when.apply(undefined, postpromises).then(function(res) {
console.log(res);
$.when.apply(undefined, validatepromises).then(function(res) {
console.log(res);
});
});*/
$.when.apply(null, [...postpromises, ...validatepromises]).then(function() {
console.log([].slice.call(arguments))
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

javascript recursive class: undefined method

I have a JavaScript class that is meant to help deal with promises. First you add functions to an array, then it executes them pops them and calls itself to do the next one. At the end of the array it resolves that promise. My hope was to then propagate the resolution all the way up the stack of recursive calls. This will allow you to force multiple asynchronous functions to run sequentially using a simple set of commands. furthermore employ logic to modify the flow of the ansync functions.
function Sequencer() {
this.functionSequence = [];
this.addFunction = function (func) {
this.functionSequence.push(func);
}
this.getFunctionSequence = function () {
return functionSequence;
}
this.executeAll = function () {
var functionList = this.functionSequence;
var deferred = $q.defer();
if (functionList.length > 0) {
functionList[0]().then(function (result) {
if (result) {
functionList.splice(0, 1);
executeAll().then(function (resultInner) {
if (resultInner == true) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
} else {
functionList = [];
deferred.resolve(false);
}
});
} else {
deferred.resolve(true);
}
return deferred.promise;
}
}
I am getting ReferenceError: 'executeAll' is undefined
in this script, on the recursive call line "executeAll' just after the splice
the first function in the array is being executed(I was testing it with a modal pop up) and when it resolves it hits the splice, then it throws the error right on the executeAll line. Am I defining the function incorrectly? Am I calling it correctly as a recursive function?
use this.executeAll - assuming this will be correct, which it wont, so you'll need to account for that as well ... something like var self = this at the top of executeAll, then call self.executeAll
this.executeAll = function() {
var functionList = this.functionSequence;
var deferred = $q.defer();
var self = this; // save reference to this
if (functionList.length > 0) {
functionList[0]().then(function(result) {
if (result) {
functionList.splice(0, 1);
// need to use self here because "this" is not the "this" we want
self.executeAll().then(function(resultInner) {
if (resultInner == true) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
} else {
functionList = [];
deferred.resolve(false);
}
});
} else {
deferred.resolve(true);
}
return deferred.promise;
};
The reason this is not the this you "want" is due to how this works in javascript - there is plenty on info on stack exchange about using this - I'll find and link a good answer shortly
I offer this alternative code
this.executeAll = function() {
return this.functionSequence.reduce(function(promise, item) {
return promise.then(function(result) {
if (result) {
return item();
}
else {
throw "Fail"; // throw so we stop the chain
}
});
}, Promise.resolve(true))
.then(function(result) {
this.functionSequence = []; // clear out the added functions
return true; // fulfilled value is true as per original code
}.bind(this), function(err) {
this.functionSequence = []; // clear out the added functions
if (err == "Fail") {
return false; // convert the "Fail" to a fullfilled value of false as per original code
}
else {
throw err; // any other error - re-throw the error
}
}.bind(this))
};

using promise with angular.js

i use angular.js in front side.
in my controller.js i defined an init() method that will be called in
init of my controller.
Init method definition:
var init = function () {
$scope.callTeamsService();
if ($scope.teams.length == 0){
....
}else{
...
}
.....
};
in $scope.callTeamsService i filled in $scope.teams variable.
$scope.callTeamsService method definition:
$scope.callTeamsService = function(){
NavService.getTeams(function (response) {
$timeout(function () {
$scope.teams = response;
}
}, 200);
});
};
In my service.js i defined a getTeams method as follow:
service.getEquipes = function (callback) {
$http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams')
.success(function (response) {
callback(response);
});
};
My problem is when $scope.teams.length == 0 condition is reached the
service.getEquipes method in my service.js is not yet called.
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
service/factory
service.getEquipes = function () {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
};
// controller
var promise = NavService.getTeams.then (
function(data) {
//assign to $scope or do logic
},
function(err){
console.log(err)
}
)
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
That's the wrong way round - you need to wait with executing the $scope.teams.length == 0 condition until the $scope.callTeamsService method has finished.
The classical method would be to give the $scope.callTeamsService method a callback parameter and call that in the timeout instead of $scope.teams = response;. Then you can put your condition in the init function in the callback that you pass.
However, you seem to want to use promises. For that, all of your functions (that all are asynchronous) should return a promise:
service.getEquipes = function (callback) {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
}
(that was easy, $http already returns promises)
$scope.callTeamsService = function() {
return NavService.getTeams().then(function(teams) {
return $timeout(function() {
return teams;
}, 200);
});
};
(and $timeout does as well - by invoking then and returning it from the callback you can chain them and get a new promise for both)
function init() {
return $scope.callTeamsService().then(function(teams) {
$scope.teams = teams;
if (teams.length == 0) {
…
} else {
…
}
});
}

Returning a value from a nested function to it's parent without using a callback

I wrote the following function to check if my HTML5 openDatabase table is filled or empty:
var that = this;
that.db = openDatabase('dbname', '1.0', "description", 1024 * 1024);
that.tableFilled = function( tableName ) {
that.db.transaction(function ( tx ) {
tx.executeSql('SELECT * FROM ' + tableName, [],
function success( c, results ) {
return ( results.rows.length > 0 ? true : false );
},
function fail() {
console.log('FAiL');
}
);
});
};
I am trying to return the true or false values to tableFilled().
Actually that.tableFilled('tableName') returns undefined.
What I am trying to achieve at the end is:
if ( that.tableFilled('tableName') ){
// ...
}
Is there a way I can return the true or false values to the parent function tableFilled() without using a callback ?
You're dealing with an asynchronous process so you can't return a value directly.
What you can do however is return a promise. Your function will promise to give you that value when it's available. To get the value out of the promise, you have to add a callback function.
You still need to use a callback function but you don't need to nest your functions anymore, you can just serialize them.
This may be way out of scope for your current needs but it's a very interesting concept. Just Google for it if you want to know more.
Here is a short example:
function my_function() {
var promise = new_promise();
do_asynchronous(function callback(result) {
promise.resolve(result); // gets called after 1 second
});
return promise;
}
var promise = my_function();
promise.done(function(result) {
console.log(result); // prints "yay!" after 1 second
});
function new_promise() {
var handlers = [];
return {
"resolve": function (result) {
for (var i = 0; i < handlers.length; i += 1) {
handlers[i](result);
}
},
"done": function (a_callback) {
handlers.push(a_callback);
}
};
}
function do_asynchronous(callback) {
setTimeout(function () {
callback("yay!");
}, 1000);
}

Chaining ajax requests with jQuery's deferred

I have a web app which must call the server multiple times. So far, I had a long nested callback chain; but I would like to use jQuery's when,then etc. functionality. However, I can't seem to get stuff running again after using a then.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then ($.get('pages/test.html'))
.done (function(args)
{
// This prints the same as the last call
alert (args);
});
What am I doing wrong? I guess its some scoping issue, as I can see the second get call being executed. Using two different args variables does not help as the argument passed to the done function is still the first get request.
As an update:
With modern jquery (1.8+) you don't need the preliminary when because get returns a Deferred Promise.
Also, pipe is deprecated. Use then instead. Just be sure to return the result of the new get which becomes the Promise attached to by subsequent then/*done*/fail calls.
So:
$.get('pages/run-tool.html')
.then (function (args) { // this will run if the above .get succeeds
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then (function() { // this will run after the above then-handler (assuming it ran)
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args) { // this will run after the second .get succeeds (assuming it ran)
alert (args);
});
All three callbacks (the two with then and the one with done) are applied to the same request – the original when call. This is because then returns the same Deferred object, rather than a new one, so that you can add multiple event handlers.
You need to use pipe instead.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.pipe (function() {
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args)
{
alert (args);
});
Here is an wonderfully simple and highly effective AJAX chaining / queue plugin. It will execute you ajax methods in sequence one after each other.
It works by accepting an array of methods and then executing them in sequence. It wont execute the next method whilst waiting for a response.
//--- THIS PART IS YOUR CODE -----------------------
$(document).ready(function () {
var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }
//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });
});
//--- THIS PART IS THE AJAX PLUGIN -------------------
$.fn.sc_ExecuteAjaxQ = function (options) {
//? Executes a series of AJAX methods in dequence
var options = $.extend({
fx: [] //function1 () { }, function2 () { }, function3 () { }
}, options);
if (options.fx.length > 0) {
var i = 0;
$(this).unbind('ajaxComplete');
$(this).ajaxComplete(function () {
i++;
if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
});
//Execute first item in queue
if (typeof options.fx[i] == "function") { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
}
}
The answer cdr gave, which has the highest vote at the moment, is not right.
When you have functions a, b, c each returns a $.Deferred() object, and chains the functions like the following:
a().then(b).then(c)
Both b and c will run once the promise returned from a is resolved. Since both then() functions are tied to the promise of a, this works similiar to other Jquery chaining such as:
$('#id').html("<div>hello</div>").css({display:"block"})
where both html() and css() function are called on the object returned from $('#id');
So to make a, b, c run after the promise returned from the previous function is resolved, you need to do this:
a().then(function(){
b().then(c)
});
Here the call of function c is tied to the promise returned from function b.
You can test this using the following code:
function a() {
var promise = $.Deferred();
setTimeout(function() {
promise.resolve();
console.log("a");
}, 1000);
return promise;
}
function b() {
console.log("running b");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("b");
}, 500);
return promise;
}
function c() {
console.log("running c");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("c");
}, 1500);
return promise;
}
a().then(b).then(c);
a().then(function(){
b().then(c)
});
Change the promise in function b() from resolve() to reject() and you will see the difference.
<script type="text/javascript">
var promise1 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("1");
def.resolve();
}, 3000);
}).promise();
};
var promise2 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("2");
def.resolve();
}, 2000);
}).promise();
};
var promise3 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("3");
def.resolve();
}, 1000);
}).promise();
};
var firstCall = function () {
console.log("firstCall");
$.when(promise1())
.then(function () { secondCall(); });
};
var secondCall = function () {
console.log("secondCall")
$.when(promise2()).then(function () { thirdCall(); });
};
var thirdCall = function () {
console.log("thirdCall")
$.when(promise3()).then(function () { console.log("done"); });
};
$(document).ready(function () {
firstCall();
});
</script>
I thought I would leave this little exercise here for anyone who may find it useful, we build an array of requests and when they are completed, we can fire a callback function:
var urls = [{
url: 'url1',
data: 'foo'
}, {
url: 'url2',
data: 'foo'
}, {
url: 'url3',
data: 'foo'
}, {
url: 'url4',
data: 'foo'
}];
var requests = [];
var callback = function (result) {
console.log('done!');
};
var ajaxFunction = function () {
for (var request, i = -1; request = urls[++i];) {
requests.push($.ajax({
url: request.url,
success: function (response) {
console.log('success', response);
}
}));
}
};
// using $.when.apply() we can execute a function when all the requests
// in the array have completed
$.when.apply(new ajaxFunction(), requests).done(function (result) {
callback(result)
});
My way is to apply callback function:
A(function(){
B(function(){
C()})});
where A, B can be written as
function A(callback)
$.ajax{
...
success: function(result){
...
if (callback) callback();
}
}

Categories