Handle AJAX nesting better with promises? - javascript

I have a nested AJAX call where each level has to wait for the previous one to finish before executing. I am using promises, but I don't see how it can help the below situation.
var me = this;
initA()
.done(function () {
initB.apply(me, arguments)
.done(function () {
initC.apply(me, arguments)
.done(function () {
initD.apply(me, arguments)
});
});
});
Is there a better way to do this the above nesting?

Use the then method and as long as your function returns a promise the promise library will try to resolve the returned promise before moving onto the next then callback. With this you can just do a bind instead of an apply.
In the example below I am using jQuery's deferred objects, but I believe it should be the same for most of the promise libraries
var me = {something:"world"};
function dotimeOut(){
console.log(this);
var def = jQuery.Deferred();
setTimeout(function(){
def.resolve(1);
},1000);
return def.promise();
}
dotimeOut()
.then(dotimeOut.bind(me))
.then(dotimeOut.bind(me))
.then(dotimeOut.bind(me));
/* This is the same as doing the below
initA()
.then(function(){
return initB.apply(me,arguments);
})
.then(function(){
return initC.apply(me,arguments);
})
.then(function(){
return initD.apply(me,arguments);
})
*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Related

Nested then() functions returning the promises earlier even before completion of nested then() function

I have a 2 functions which should call one after the other Like below.
MainFunc().then(DrawChart());
MainFunc() function internally have nested functions like I have mentioned below.
I want MainFuc() to return promise or in other way DrawChart() function should call once the createMultiBatchDropdown() is completed.
I checked some links : Nesting asynchronous jquery promises
But I dont want to use any set timeout or delay functions.
I'm new to the concept of this then() and promise() function.Any help will be appreciated.
function MainFunc(){
var r = $.Deferred();
var xhr = BatchTypeFilterList(data,id).then(function(res){
//Logic goes here
var impactXhr = GetImpactData(id).then(function(result){
var DropXhr = createMultiBatchDropdown('ImpactBatchSearch',result)
})
})
return r.promise(xhr);
}
function BatchTypeFilterList(){
var deferred = $.Deferred();
var xhr = $.ajax({
//Ajax Call
success:function(result){
deferred.resolve(result);
}
})
return deferred.promise(xhr);
}
function GetImpactData(){
var deferred = $.Deferred();
var xhr = $.ajax({
//Ajax Call
success:function(result){
deferred.resolve(result);
}
})
return deferred.promise(xhr);
}
function createMultiBatchDropdown(){
var batchDropDownDeferred = $.Deferred();
//No ajax call normal jquery logic to form dropdown
batchDropDownDeferred.resolve(data);
return batchDropDownDeferred.promise(xhr);
}
GetImpactData returns a Promise, but it's not chained with the outer xhr, which means that calling MainFunc will result in a Promise that resolves before createMultiBatchDropdown has been called. Instead, return the Promise created by createMultiBatchDropdown so that it's chained with the whole Promise chain properly. You also need to return impactXhr to complete the chain. Change to:
var xhr = BatchTypeFilterList(data, id).then(function(res) {
//Logic goes here
var impactXhr = GetImpactData(id).then(function(result) {
return createMultiBatchDropdown('ImpactBatchSearch', result);
})
return impactXhr;
})

What is the best way to implement promise in the given scenario?

I'm using D.js as promise library for our javascript application.
Following is my sample code:
function getData(deferred) {
var data_one;
// getInfo is returning a promise for async task
getInfo()
.then(function (resp_one) {
data_one = resp_one;
// getInfo2 is also returning another promise
return getInfo2();
})
.then(function (resp_two) {
deferred.resolve('prefix' + data_one + resp_two);
});
};
function sample () {
var d = D(),
data = localStorage.getItem('key');
if (data) {
d.resolve(data);
} else {
getData(d);
}
return d.promise;
}
sample().then(function (data) {
//do something with data.
});
I im invoking sample function. Is the implementation inside the sample function and sub functions following the coding standard for promises?
Im new to promises, Is it good to passing around deferred object to other function to resolve/reject ??
Is there any better way to implement the above functionality??
Thanks in advance..
Looks like you can improve the code if you use promises in more natural way.
First of all if getData returns a Promise then you don't have to pass deferred around, this is considered anti-pattern. You just simply return getInfo().
Another thing, in the sample function if your data might be already available it's convenient to use D.promisify method to return resolved promise with non-promise value:
function getData() {
var data_one;
return getInfo().then(function (resp_one) {
data_one = resp_one;
return getInfo2();
})
.then(function (resp_two) {
return 'prefix' + data_one + resp_two;
});
};
function sample() {
var data = localStorage.getItem('key');
return data ? D.promisify(data) : getData();
}
sample().then(function (data) {
//do something with data.
});

Getting correct data when using multiple deferred ajax calls

I have a function that uses two ajax calls in order to get the proper information:
var getUsers = function() {
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js", function(foo) {
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js", function(bar) {
return foo['age'] = bar.type;
});
});
}
And an outside function that calls the current function and only continues when the calls are finished.
getUsers().then(function(result) {
// ...
});
Now the weird thing is that if I display the result, the 'age' will show up in the console, but if I try to access it using result['age'], it will return undefined.
Is there a proper way of handling multiple deferred calls?
Code
http://codepen.io/norbiu/pen/bNRQxL
Edit Instead of using a separate deferred, you can chain the ones returned from getJSON() like this
var getUsers = function() {
var foo;
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.then(function(data) {
foo = data;
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
}).then(function(bar) {
foo['age'] = bar.type;
return foo;
});
}
Note: you need to save the return value from the first call or it won't be accessible to the second.
Original code for posterity
You can use a jQuery Deferred object and return that instead
var getUsers = function() {
var dfd = $.Deferred();
$.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.done(function(foo) {
$.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.done(function(bar) {
foo['age'] = bar.type;
dfd.resolve(foo);
}).fail(function(e) {
dfd.reject(e);
})
}).fail(function(e) {
dfd.reject(e);
});
return dfd.promise();
}
http://codepen.io/anon/pen/pvwqZo
The deferred object won't resolve until both requests succeed (and will fail if any of them fail).

How can they call def.then here?

I was reading about promises and found this fiddle created by the author of this post
The code is here:
var def, getData, updateUI, resolvePromise;
// The Promise and handler
def = new $.Deferred();
updateUI = function (data) {
$('p').html('I got the data!');
$('div').html(data);
};
getData = $.ajax({
url: '/echo/html/',
data: {
html: 'testhtml',
delay: 3
},
type: 'post'
})
.done(function(resp) {
return resp;
})
.fail(function (error) {
throw new Error("Error getting the data");
});
// Event Handler
resolvePromise = function (ev) {
ev.preventDefault();
def.resolve(ev.type, this);
return def.promise();
};
// Bind the Event
$(document).on('click', 'button', resolvePromise);
def.then(function() {
return getData;
})
.then(function(data) {
updateUI(data);
})
.done(function(promiseValue, el) {
console.log('The promise was resolved by: ', promiseValue, ' on ', el);
});
// Console output: The promise was resolved by: click on <button> </button>
I do understand from the first part of this series that a deferred has a promise which can be exposed using the promise method on it.
Promises have then method which returns a promise for chaining.
Here they resolve the promise on the deferred in resolvePromise,then the then method on the deferred which I dont think is a promise is executed.What am I missing here?
Deferred objects in jQuery are also thenables and you can use them in place of promises. Doing so is rather uncommon though.
var d = $.Deferred().resolve();
d.then(function(){
console.log("HI"); // this will run.
});
The original $.ajax having .done and .fail is pointless in this case, especially the .done whose return value is ignored and has no impact.
In all honestly, I think the code could be improved to something like rather easily:
var getData = $.post('/echo/html/', { html: 'testhtml', delay: 3 });
var d = $.Deferred();
$(document).on('click', 'button', function(ev){
d.resolve();
return false;
});
$.when(d, getData).then(function(_, data){
$('p').html('I got the data!');
$('div').html(data);
});
There is no point in .thening if you only use the identity function (that is, return the same thing and do nothing else.
There is no point in .doneing only to return the same thing.
Generally, I would advise against promises for handing events unless the events are strictly one time.

jQuery Deferreds with Chained Saves, Unified Result

I have two objects, ObjectA and ObjectB. I want to save ObjectB only after ObjectA is done, but I want to return a promise which wraps the result of both of them.
Here's my first hack at it to show the functionality that I want. This function works fine it's just ugly and surely there's a better way.
Functions saveObjectA and saveObjectB both return $.post() promises.
saveAAndBSequentially: function () {
var dfd = $.Deferred();
saveObjectA().done(function () {
saveObjectB().done(function () {
dfd.resolve();
}).fail(function () {
dfd.reject();
});
}).fail(function () {
dfd.reject();
});
return dfd.promise();
}
I'd just use $.when and add a done callback on saveObjectA to trigger saveObjectB, but the deferred for saveObjectB doesn't exist yet so I don't believe I can use $.when on it right away.
Ideas on how to solve this is a more elegant manner are greatly appreciated!
.pipe() does exactly the task you have handcoded:
var saveAAndBSequentially = function () {
return saveObjectA().pipe(function () {
return saveObjectB();
});
};

Categories

Resources