not asynchronous function executed as jQuery Deferred - javascript

Lets say I want to process some tasks in the synchronous manner, so I have this function:
function executePromiseQueueSync(queue){
var seed = $.Deferred(),
finalPromise;
finalPromise = _.reduce(queue, function(memo, promise){
return memo.then(function(){
return promise.funct.apply(null, promise.argmnt);
});
}, seed.promise());
seed.resolve();
return finalPromise;
}
Now I can use it to process some files:
_.each(fileList, function(element, index, list){
_.each(element, function(el, idx, lst){
promisesQueue.push({funct: processFile, argmnt:[el, index + (len - fileList.length) ,len]});
});
});
Execute it and indicate a progress:
executePromiseQueueSync(promisesQueue).then(function(){
....
}, function(){
....
}).progress(function(msg, progress, name, index, status, desc){
console.log('progress');
});
Process function itself looks like this:
function processFile(file, index, size)
{
var dfd = new jQuery.Deferred();
if (file.name.match('(.*)\\.jpg'))
...
else if
...
else
$.when(processWrongFileType(file)).then(function(){
dfd.notify(...);
dfd.resolve();
});
return dfd.promise();
}
as you see there is nothing much to do when the file has a wrong type:
So sometimes I would like to execute synchronous code just like a promise:
function processWrongFileType(){
var dfd = new jQuery.Deferred();
dfd.resolve();
console.log("blah");
return dfd.promise();
}
The problem is if processWrongFileType will be executed, notify will not work.
If I change processWrongFileType to look like this:
function processWrongFileType()
{
var dfd = new jQuery.Deferred();
setTimeout(function(){dfd.resolve();},1);
return dfd.promise();
}
notify() will work. Is there any way to avoid setTimeout and still have notify() working with progress event?

You dont need to do anything special in order to use sync code as promise.
Just return value that is ==true
$.when((function() {
return prompt('really?')
})()).then((function() {
return alert('yeah')
})()).done((function () {
alert('done')
})())

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>

How to efficiently test deferred always

When writing tests for code in a jQuery ajax (or get) always part or even in bluebird promises finally like this:
function doStuff() {
console.log('stuff done');
}
function someFunction() {
return $.get('someurl').always(doStuff);
}
I always find myself writing (QUnit) tests for this like:
QUnit.test("doStuff will be called when someFunction succeeds", function (assert) {
var deferred = $.Deferred();
var backup = $.get;
$.get = function () { return deferred; };
var doStuffIsCalled = false;
doStuff = function(){ doStuffIsCalled = true; };
deferred.resolve({});
return someFunction().then(function(){
$.get = backup;
assert.ok(doStuffIsCalled);
});
});
QUnit.test("doStuff will be called when someFunction fails", function (assert) {
var deferred = $.Deferred();
var backup = $.get;
$.get = function () { return deferred; };
var doStuffIsCalled = false;
doStuff = function(){ doStuffIsCalled = true; };
deferred.reject(new Error('some error'));
return someFunction().catch(function(){
$.get = backup;
assert.ok(doStuffIsCalled);
});
});
This works, but is somewhat verbose. Is there some more efficient way, preferrably in a single test, to directly test code called in the always part of a deferred?
You can use Sinon.js to mock jQuery ajax (or get) as well as promises in general.
One approach could be:
function someFunction() {
return $.get('/mytest').always(doStuff);
}
function givenFncExecutesAndServerRespondsWith(reponseNumber, contentType, response) {
server.respondWith("GET", "/mytest", [reponseNumber, contentType, response]);
someFunction();
server.respond();
}
module("Testing server responses", {
setup: function () {
server = sinon.sandbox.useFakeServer();
doStuff = sinon.spy();
},
teardown: function () {
server.restore();
}
});
test("doStuff will be called when someFunction succeeds", function () {
givenFncExecutesAndServerRespondsWith(200, '', '');
ok(doStuff.called, "spy called once");
});
test("doStuff will be called when someFunction fails", function () {
givenFncExecutesAndServerRespondsWith(500, '', '');
ok(doStuff.called, "spy called once");
});
You can play with this code in this fiddle. If instead of always you used done or fail for calling the callback, the corresponding test would fail.
The explanation to the code would be as follows:
Create a fake server and a spy that will act as the always callback.
Modify the response number of the server's response according to what we're testing.
Hope it helps.

How to call a function containing chained promises

I am chaining multiple asynchronous function calls using promises and the Q js library. My current code looks like this:
function user() {
getID()
.then(getName);
}
function getID() {
var deferred = Q.defer();
asyncCall(arg, function(data) {
deferred.resolve(data);
});
return deffered.promise;
}
function getName(ID) {
var deferred = Q.defer();
asyncCall2(arg, function(data) {
deferred.resolve(data);
});
return deffered.promise;
}
I am trying to call user() from a different location and have it return the result of getName but am not sure how to do this.
just return the value (which is a promise itself):
function user() {
return getID().then(getName);
}
and later you can use it as the rest of your code:
user().then(function(result) {});

Jquery Deferred Not reaching last .then

I have a sequence of functions that must be executed. They all execute sequentially except the last one. d1 executes, d2 executes, d3 executes then the code inside the done function executes before the resolve of d4. Can't figure out why. Any help would be appreciated.
$(document).ready(function() {
var d1 = functiond1();
var d2 = functiond2();
var d3 = functiond3();
var d4 = functiond4();
d1.then(d2).then(d3).then(d4).done(function() {
//Code here does not wait for d4 to end before executing
//HELP!
});
});
function functiond1() {
var dfd = $.Deferred();
//Do stuff here
//Works in sequence
dfd.resolve();
return dfd.promise();
}
function functiond2() {
var dfd = $.Deferred();
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
jQuery.getJSON($.webMethoJSONGet1, params).done(function(data) {
//Do stuff here
//Works in sequence
dfd.resolve();
});
return dfd.promise();
}
function functiond3() {
var dfd = $.Deferred();
//Do stuff here
//Works in sequence
dfd.resolve();
return dfd.promise();
}
function functiond4() {
var dfd = $.Deferred();
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
jQuery.getJSON($.webMethoJSONGet2, params).done(function(data) {
//Do stuff here
//does not work in sequence
dfd.resolve();
});
return dfd.promise();
}
It's hard to tell what you are trying to do with those promises. You first call all 4 functions, and then you try to chain them with a bunch of then callbacks. If you want to sequentially chain them together it should look like this:
functiond1()
.then(functiond2)
.then(functiond3)
.then(functiond4)
.done(function() { /* blah */ });
If you just want a result after all have completed you can use $.when
$.when(functiond1(), functiond2(), functiond3(), functiond4())
.then(function(resultd1, resultd2, resultd3, resultd4) { /* blah */ });
On another note, in your functions you create promises that are resolved inside the done callback of another promise which is unnecessary. The $.getJSON.done() calls return a promise themselves so an additional promise is not needed. Just return the promise returned from done().
Sorry, I haven't messed much with jQuery deferred objects, but they appear similiar enough to standard Promises.
To run the functions in sequence, you need to pass references to the functions within the .then chain, and not the results of calling those functions.
e.g.
var d1 = functiond1; // NB: no ()
...
d1.then(d2).then(d3).then(d4).done(...);
functiond1().then(functiond2).then(functiond3).then(functiond4).done(...)
The ultimate cause of your problem is that invoking d4 straight away will cause its resolved promise to pass-thru to .done immediately irrespective of the state of the earlier part of the .then chain.
You should also not wrap your JSON functions with additional promises, since $.getJSON already returns a promise that will be rejected if the AJAX query fails:
function functiond4() {
...
return $.getJSON(...);
}
I was facing the same issue on a project, this solution with an array works well:
$(document).ready(function() {
var pr = [];
var d1 = functiond1();
var d2 = functiond2();
var d3 = functiond3();
var d4 = functiond4();
function functiond1() {
var dfd = $.Deferred();
pr.push(dfd);
setTimeout(function(){
$('body').append('1 resolved <br>');
dfd.resolve();
}, 2000);
}
function functiond2() {
var dfd = $.Deferred();
pr.push(dfd);
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
setTimeout(function(){
$('body').append('2 resolved <br>');
dfd.resolve();
}, 3000);
}
function functiond3() {
var dfd = $.Deferred();
pr.push(dfd);
setTimeout(function(){
$('body').append('3 resolved <br>');
dfd.resolve();
}, 1000);
}
function functiond4() {
var dfd = $.Deferred();
pr.push(dfd);
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
setTimeout(function(){
$('body').append('4 resolved <br>');
dfd.resolve();
}, 50);
}
$.when.apply($, pr).then(function() {
// do something
$('body').append('proceed with code execution');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

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