multiple chain of when() then() - defered object - javascript

I want to do a chain of $.when().then($when().then(...)) in jquery. I'm not used to the defered functions, first time using them.
I changed my code a bit my code to represent this situation:
function setenum(e)
{
return $.ajax({
url: SetPathUrl1(),
type: 'GET',
data: { isNew: e.isNew() },
contentType: 'application/json; charset=utf-8',
success: function (data, status, xhr) {
/*My stuff*/
},
error: function (xhr, status, error) {
/*My stuff*/
}
});
}
function setdropdown1(e)
{
return $.ajax({
url: SetPathUrl2(),
type: 'GET',
data: { isNew: e.isNew() },
contentType: 'application/json; charset=utf-8',
success: function (data, status, xhr) {
/*Fill my first ddl based on enum*/
},
error: function (xhr, status, error) {
/*My stuff*/
}
});
}
function setdropdown2(e)
{
return $.ajax({
url: SetPathUrl3(),
type: 'GET',
contentType: 'application/json; charset=utf-8',
success: function (data, status, xhr) {
/*Fill my second ddl based on enum*/
},
error: function (xhr, status, error) {
/*My stuff*/
}
});
}
function DoOtherStuff(e)
{
/**/
}
function MainNotWorking(ImportantModel)
{
$.when(setenum(ImportantModel))
.then(
$.when(setdropdown1(ImportantModel),setdropdown2(ImportantModel))
.then(
function () {
DoOtherStuff(e);
}
)
);
}
function MainWorking(ImportantModel)
{
$.when(setenum(ImportantModel),setdropdown1(ImportantModel),setdropdown2(ImportantModel))
.then(
function () {
DoOtherStuff(e);
}
);
}
MainNotWorking : the order is not respected at all, set setdropdown1 and setdropdown2 are called sometimes before the setenum.
MainWorking:
When I have only one level of when, then the function DoOtherStuff is called before all other functions, but it's only one level. I want to do multiple chain setenum before setdropdown1 and setdropdown2 and then finally DoOtherStuff.

Use $.when().done(callback)
function MainNotWorking(ImportantModel) {
$.when(
setenum(ImportantModel)
).done(function() {
$.when(
setdropdown1(ImportantModel),
setdropdown2(ImportantModel)
).done(DoOtherStuff);
});
}

First, you should pass a function reference to then:
.then(function() { ... })
What you are passing is executed immediately, instead of when the first function is finished.
That alone would be enough, but if you wanted to "flatten" out the chaining, you could do something like this:
$.when(
setenum(ImportantModel)
).then(function() {
return $.when(
setdropdown1(ImportantModel),
setdropdown2(ImportantModel)
)
}).then(function() {
DoOtherStuff();
});
Here's a demo: http://jsfiddle.net/44e5Y/

Related

How to spy/stub with mocha/chai on ajax .fail() and .done()?

I am new to mocha/chai unit testing. I have following ajax call which is making a service call. I have added .fail() and .done() as a part of ajax call.
So I am not sure what I am missing here. Sorry if I am missing basic things here. Can someone please describe what's wrong here?
function searchAPIcall(endPointurl, method, search_headers, search_identifier) {
$.ajax({
type: method,
contentType: "application/json; charset=utf-8",
dataType: "json",
url: endPointurl,
headers: search_headers,
xhrFields: {
withCredentials: true
},
success: function success(results, xhr, XMLHttpRequest) {
successCallback();
},
error: function (xhr, textStatus, error) {
callErrorFunc();
}
}).fail(function (xhr, textStatus, errorThrown) {
callFailFunc();
}).done(function () {
callDoneFunction();
});
}
I have written following unit test:
describe.only('Testing searchdoctorAPIcall function', function () {
var testurl = 'https://abc.serve.org/getData';
beforeEach(function() {
sinon.stub($, 'ajax');
});
afterEach(function() {
$.ajax.restore();
});
it('should make an ajax call', function(done) {
searchAPIcall(testurl, "GET", {"Content-Type":"application-json;charset=utf-8",X-Id":"1da9b0c8-bb52"}, "baseSearch");
expect($.ajax.calledOnce).to.be.true;
done();
});
});
I am always getting the following error while executing unit test.
undefined is not an object (near '...} }).fail(function (xhr,...')
But When I change my searchAPIcall function and remove the .dail() and .done() unit test is passing.
function searchAPIcall(endPointurl, method, search_headers, search_identifier) {
$.ajax({
type: method,
contentType: "application/json; charset=utf-8",
dataType: "json",
url: endPointurl,
headers: search_headers,
xhrFields: {
withCredentials: true
},
success: function success(results, xhr, XMLHttpRequest) {
successCallback();
},
error: function (xhr, textStatus, error) {
callErrorFunc();
}
})
}
Now if I run the unit test then it is passing.
should make an ajax call (passed)
You need to weave the test done call into your code so that it is run after the async call and your tests finish:
function searchAPIcall(endPointurl, method, search_headers, search_identifier) {
return $.ajax({ // return the call so it can be chained
...
});
}
it('should make an ajax call', function(done) {
searchAPIcall(
testurl,
"GET",
{"Content-Type":"application-json;charset=utf-8","X-Id":"1da9b0c8-bb52"}, // Note: you are missing a " before the 'X' in this line
"baseSearch"
).done(() => {
expect($.ajax.calledOnce).to.be.true;
done();
}).fail(done); // Equivalent to: .fail(error => done(error))
});

How to make Javascript run function synchronously [duplicate]

This question already has answers here:
jQuery - Execute 2 functions synchronously
(2 answers)
Closed 5 years ago.
In my asp.net mvc app, I have button click that points to a javascript which calls
function OnButtonClick(s, e, startUrl, progressUrl) {
Fetch(progressUrl);
ImportUpdate(startUrl);
}
Fetch and ImportUpdate are ajax jquery to a controller action.
function Fetch(progressUrl) {
positionDate = ReportingPositionDate.GetDate().toDateString();
$.ajax({
type: 'POST',
url: "#Url.Action("BloombergFet", "ImportData")",
data: JSON.stringify({ positionDate: positionDate }),
dataType: "text",
contentType: "application/json; charset=utf-8",
beforeSend: function () { lpBloomberg.Show(); },
success: function (msg) {
ImportSuccessMessage.SetText(msg);
lpBloomberg.Hide();
lpImport.Show();
},
error: function (xhr, textStatus, errorThrown) {
lpBloomberg.Hide()
}
});
}
function ImportUpdate(progressUrl) {
positionDate = ReportingPositionDate.GetDate().toDateString();
myProgressBar.Show;
$.ajax({
type: 'POST',
url: "#Url.Action("ProcessImportRecord", "ImportData")",
data: JSON.stringify({ positionDate: positionDate }),
dataType: "text",
contentType: "application/json; charset=utf-8",
beforeSend: function () { lpImport.Show(); },
success: function (msg) {
ImportDataGridView.PerformCallback();
ImportSuccessMessage.SetVisible(true);
ImportSuccessMessage.SetText(msg);
lpImport.Hide();
},
error: function (xhr, textStatus, errorThrown) {
ImportErrorMessage.SetVisible(true);
ImportErrorMessage.SetText(xhr.statusText)
}
});
}
Currently both the methods Fetch(progressUrl) and ImportUpdate(progressUrl) are called at the same time. I want Fetch(progressUrl) to complete and then ImportUpdate to run.
How do I achieve this. Appreciate all help.
Call your second function ImportUpdate(progressUrl) in the success block of the first function Fetch(progressUrl) like so:
function Fetch(progressUrl) {
positionDate = ReportingPositionDate.GetDate().toDateString();
$.ajax({
type: 'POST',
url: "#Url.Action("BloombergFet", "ImportData")",
data: JSON.stringify({ positionDate: positionDate }),
dataType: "text",
contentType: "application/json; charset=utf-8",
beforeSend: function () { lpBloomberg.Show(); },
success: function (msg) {
ImportSuccessMessage.SetText(msg);
lpBloomberg.Hide();
lpImport.Show();
//Place call for ImportUpdate function here, like so
ImportUpdate(startUrl);
},
error: function (xhr, textStatus, errorThrown) {
lpBloomberg.Hide()
}
});
}
However, like James pointed out, if you want to call ImportUpdate after every time that Fetch is called, it makes more sense to just combine them UNLESS you call ImportUpdate independently somewhere else, when Fetch is not called first.
BTW, the callbacks Kevin B. is probably referring to are used with the jQuery .post() function, which you can use like this:
// Assign handlers immediately after making the request,
// and remember the jqxhr object for this request
var jqxhr = $.post( "example.php", function() {
alert( "success" );
})
.done(function() {
alert( "second success" );
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
});
// Perform other work here ...
// Set another completion function for the request above
jqxhr.always(function() {
alert( "second finished" );
});
so instead of putting your function call in the success callback of your current Fetch function you'd put it in .done callback like so:
.done(function() {
ImportUpdate(startUrl);
})
.fail(function() {
//handle errors
})
Put ImportUpdate(progressUrl) inside your success callback function for Fetch(progressUrl)

using $.when to execute first ajax then execute the next in $.when().done

I know this has been asked multiple times but I'm having a problem executing $.ajax by order using $.when.
In the sample code below, I want to run the first ajax inside $.when() then the second $.ajax inside .done().
var api = {
getFoo: function(callback) {
$.when(this.getBar()).done(function() {
chrome.storage.local.get(['bar'], function(data) {
// omitted some code here for brevity
$.ajax({
type: 'GET',
url: /api/foo/,
dataType: 'json'
}).done(function(result) {
console.log('success', 'went here!');
}).fail(function(request) {
console.log('failed', 'went here!');
});
});
});
},
getBar: function() {
chrome.storage.local.get(['data1'], function(data) {
if(typeof data['data1'] !== 'undefined') {
// omitted some code here for brevity
$.ajax({
type: 'POST',
url: /api/bar/,
data: data['data1'],
dataType: 'json'
}).done(function(result) {
console.log('success', 'went here!');
}).fail(function(xhr, status, error) {
console.log('failed', 'went here!');
});
}
});
}
}
When I ran the code, the second $.ajax execute immediately without waiting for the first $.ajax to finish. Please, could anyone redirect me to the right way?
We need to return a promise for async actions
try this:
getFoo: function(callback) {
this.getBar().then(function(){
chrome.storage.local.get(['bar'], function(data) {
// omitted some code here for brevity
$.ajax({
type: 'GET',
url: /api/foo/,
dataType: 'json'
}).done(function(result) {
console.log('success', 'went here!');
}).fail(function(request) {
console.log('failed', 'went here!');
});
});
});
},
getBar: function() {
var deferred = $q.defer();
chrome.storage.local.get(['data1'], function(data) {
if(typeof data['data1'] !== 'undefined') {
// omitted some code here for brevity
$.ajax({
type: 'POST',
url: /api/bar/,
data: data['data1'],
dataType: 'json'
}).done(function(result) {
deferred.resolve(result);
}).fail(function(xhr, status, error) {
deferred.reject(error);
});
}
});
return deferred.promise;
}
There is a good example at jQuery - when() page. Here is a short example:
var api = {
flag: $.Deferred(),
getFoo: function(callback) {
$.when(this.flag).done(function() {
console.log('getFoo');
});
},
getBar: function() {
console.log('getBar');
this.flag.resolve();
}
}

Double ajax request response

I'm using ajax successives requests and I need do a callback when all the successives requests are done
function doAjaxRequest(data, id) {
// Get payment Token
return $.ajax({
type: "POST",
url: 'exemple1.php',
data: data
success: function(msg){
$.ajax({
type: "POST",
url: 'exemple2.php',
data: msg,
success: function(msgr) {
document.getElementById(id).value=msgr;
},
error:function (xhr, status, error) {
//Do something
}
});
},
error:function (xhr, status, error) {
//Do something
}
});
}
$.when(
doAjaxRequest(data, "input-1"),
doAjaxRequest(otherData, "input-2")
).done(function(a1, a2){
//Need do something when both second ajax requests (example2.php) are finished
}
With this code, the done function is call before my calls to "exemple2.php" are succeeded.
How can I wait for that?
Thanks for answering!
function doAjaxRequest(data, id) {
// Get payment Token
return new Promise(function(resolve,reject){
$.ajax({
type: "POST",
url: 'exemple1.php',
data: data
success: function(msg){
$.ajax({
type: "POST",
url: 'exemple2.php',
data: msg,
success: function(msgr) {
document.getElementById(id).value=msgr;
resolve();
},
error:function (xhr, status, error) {
//Do something
reject();
}
});
},
error:function (xhr, status, error) {
//Do something
reject();
}
});
});
}
Promise.all([
doAjaxRequest(data, "input-1"),
doAjaxRequest(otherData, "input-2")])
.then(function(values){
//Need do something when both second ajax requests (example2.php) are finished
}
Your sub ajax request is independant of the first ajax result, then the call to example2 is completely separated from the $.when() promise.abort
Just try to use the fact that jquery $.ajax return promise like object
Here my code from plnkr
// Code goes here
function doAjaxRequest(data, id) {
// Get payment Token
return $.ajax({
type: "GET",
url: 'example1.json',
data: data
}).then(function(msg, status, jqXhr) {
return $.ajax({
type: "GET",
url: 'example2.json',
data: msg
});
}).done(function(msgr) {
console.log(msgr);
return msgr;
});
}
var data = {foo:'bar'};
var otherData = {foo2:'bar2'};
$.when(
doAjaxRequest(data, "input-1"),
doAjaxRequest(otherData, "input-2")
).done(function(a1, a2) {
console.log(a1, a2);
//Need do something when both second ajax requests (example2.php) are finished
});
Attention, I replace POST by GET and use exampleX.json files for my tests on plnkr
You can test it here : https://plnkr.co/edit/5TcPMUhWJqFkxbZNCboz
Return a custom deferred object, e.g:
function doAjaxRequest(data, id) {
var d = new $.Deferred();
// Get payment Token
$.ajax({
type: "POST",
url: 'exemple1.php',
data: data
success: function(msg){
$.ajax({
type: "POST",
url: 'exemple2.php',
data: msg,
success: function(msgr) {
document.getElementById(id).value=msgr;
d.resolveWith(null, [msgr]); // or maybe d.resolveWith(null, [msg]);
},
error:function (xhr, status, error) {
//Do something
d.reject();
}
});
},
error:function (xhr, status, error) {
//Do something
d.reject();
}
});
return d;
}
Now, i'm not sure what is your expected datas passed to $.when().done() callback.

show() overwritten multiple ajax calls

I have one html element (elem1) and 2 JS functions (func1, func2) that hides and shows elem1 respectively. These JS functions make individual ajax calls and func2 is calling func1 internally.
Problem: I need to call func2, which internally calls func1. Calling func1 hides elem1. After calling func1, I want to show elem1. But this show is not working.
JSFiddle: https://jsfiddle.net/46o93od2/21/
HTML:
<div id="elem">
Save ME
</div>
<br/>
<button onclick="func1()" id="func1">Try Func1</button>
<button onclick="func2()" id="func2">Try Func2</button>
JS:
function func1() {
$.ajax({
url: '/echo/json/', //use the correct processing url here
type: "POST",
data: {}, // send in your data
success: function (data) {
//var aData = JSON.parse(data); // there is no data to parse
$('#elem').hide();
},
error: function (xhr, errmsg, err) {
alert('error');
}
});
}
function func2() {
$.ajax({
url: '/echo/json/', //use the correct processing url here
type: "POST",
data: {}, // send in your data
success: function (data) {
//var aData = JSON.parse(data); // there is no data to parse
func1();
$('#elem').show();
},
error: function (xhr, errmsg, err) {
alert('error');
}
});
}
Make func1 take a callback function that tells it what to do after it gets the response. func2 can pass a function that shows the element.
function func1(callback) {
$.ajax({
url: '/echo/json/', //use the correct processing url here
type: "POST",
data: {
json: ''
}, // send in your data
success: function(data) {
if (callback) {
callback();
} else {
$('#elem').hide();
}
},
error: function(xhr, errmsg, err) {
alert('error');
}
});
}
function func2() {
$.ajax({
url: '/echo/json/', //use the correct processing url here
type: "POST",
data: {
json: ''
}, // send in your data
success: function(data) {
func1(function() {
$('#elem').show();
});
},
error: function(xhr, errmsg, err) {
alert('error');
}
});
}
DEMO

Categories