jQuery Deferreds with Chained Saves, Unified Result - javascript

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();
});
};

Related

Is $.when() or $.when.apply() the correct call for getting a promise out of a forEach?

I have a function processing an array, calling a function for each entry within the array with a Promise returned, and then finally returning that it's done.
var dealCardSelectableAI = function () {
var deferred = $.Deferred();
var deferredQueue = [];
_.forEach(model.galaxy.systems(), function (system, starIndex) {
if (model.canSelect(starIndex) && system.star.ai()) {
deferredQueue.push(
chooseCards({
inventory: inventory,
count: 1,
star: system.star,
galaxy: game.galaxy(),
addSlot: false,
}).then(function (result) {
system.star.cardList(result);
})
);
}
});
$.when.apply($, deferredQueue).then(function () {
deferred.resolve();
});
return deferred.promise();
};
dealCardSelectableAI().then( /* other stuff */ )
Most stuff I find on promises is to do with ES6 and Promises.all(), but I'm working in ES5 so am using jQuery.
My understanding is that due to deferredQueue being of unknown length $.when.apply() is the correct call here rather than $.when(), but regardless of which I use everything seems to operate as expected.
Have I set this up correctly? Is $.when.apply() the correct call and am I using it right?

Jquery .then not working

I have an accordion, and want to trigger something when it has finished transitioning from one state to another. The following code is throwing up an error Uncaught TypeError, I am just trying to console.log when it has finished for now:
$(document).ready(function () {
$('.accordion-tabs').each(function() {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$('.accordion-tabs').on('click', 'li > a.tab-link', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs');
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active').then(
function() {
console.log( "Accordion Finished" );
});
} else {
event.preventDefault();
}
});
});
Where am I going wrong? This is the first time I have used .then!
yes it's not working, this is not the way of using it you need to learn promises first
It's used to replace (or provide an alternate way) the old callback mechanism with a cleaner way to handle asynchronous requests, instead of passing your callbacks as parameters, you can chain your function with .then, given function will be executed once the promise gets resolved.
Anyhow, this is just a basic explanation, you should really get into the books of promises for more info.
a simple example of :
var promise = new Promise(function(resolve, reject) {
if (true /* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
return 123;
}).then(function (x){
console.log(x);
}).then(function (x){
console.log(x)
})

Handle AJAX nesting better with promises?

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>

Managing Synchronous and Asynchronous JavaScript Functions with jQuery's Deferred

I need to run a series of calls over websockets via Socket.IO (client-side). Since I'm not using $.ajax, jQuery's deferred functions won't integrate as well and I'll have to manually handle promises. With every websocket call, I pass a callback and I'm quickly seeing how this project could spiral out of control. Here's a simplified example of how my websocket calls work (excluding all connection handling code):
function js2node(nodeFunction, data, callback){
socket.emit('incoming', nodeFunction, data, callback);
}
function sampleServerCall(){
js2node('sampleCall', 'something', 'sampleCallback');
}
function sampleCallback(json){
// Handle data
}
sampleServerCall();
I will be talking to the server quite a bit, all calls will be asynchronous, but some will need to come back in a specific order. Enter jQuery deferred. Here is some working code:
var deferredArray = [];
$(function(){
$.when( // Any order
getData1(),
getData2()
).then(function(){ // Must have responses from dataCallback1 and dataCallback2 before doing this...
$.when( // Any order
getData3(),
getData4()
).then(function(){ // Must have responses from dataCallback3 and dataCallback4 before doing this...
getData5();
});
});
});
function getData1(){
js2node('data1', 'something', 'dataCallback1');
deferredArray[0] = new $.Deferred();
return deferredArray[0].promise();
}
function getData2(){
js2node('data2', 'something', 'dataCallback2');
deferredArray[1] = new $.Deferred();
return deferredArray[1].promise();
}
function getData3(){
js2node('data3', 'something', 'dataCallback3');
deferredArray[2] = new $.Deferred();
return deferredArray[2].promise();
}
function getData4(){
js2node('data4', 'something', 'dataCallback4');
deferredArray[3] = new $.Deferred();
return deferredArray[3].promise();
}
function getData5(){
js2node('data5', 'something', 'dataCallback5');
deferredArray[4] = new $.Deferred();
return deferredArray[4].promise();
}
function dataCallback1(json){
// Handle data
deferredArray[0].resolve();
}
function dataCallback2(json){
// Handle data
deferredArray[1].resolve();
}
function dataCallback3(json){
// Handle data
deferredArray[2].resolve();
}
function dataCallback4(json){
// Handle data
deferredArray[3].resolve();
}
function dataCallback5(json){
// Handle data
deferredArray[4].resolve();
}
As you can see, I'm still stuck with nested callbacks from the way I'm using when/then and nesting could potentially go deeper as I add functionality. Deferred is a new concept to me but I've read it's supposed to help in situations such as this. I feel like there has to be a better way than what I'm currently doing. Can anyone help me set this up more efficiently?
You can do more with .then:
$(function(){
$.when(
doSock('data1', 'something'),
doSock('data2', 'something')
).then(function(data1, data2){
return $.when(
doSock('data3', 'something'),
doSock('data4', 'something')
);
}).then(function(data3, data4){
return doSock('data5', 'something');
});
});
That way your nesting never goes deeper than that.
(i used adeneo's helper method)
Using a better helper function sure would help, but you'd still have to structure the calls with $.when and $.then to execute them in the proper order
function doSock(nodeFunction, data) {
var def = new $.Deferred();
socket.emit('incoming', nodeFunction, data, function(received) {
def.resolve(received)
});
return def.promise();
}
$(function(){
$.when(
doSock('data1', 'something'),
doSock('data2', 'something')
).then(function(data1, data2){
$.when(
doSock('data3', 'something'),
doSock('data4', 'something')
).then(function(data3, data4){
doSock('data5', 'something');
});
});
});

Delay Between Functions

this is my code snippet.
function customFadeIn () {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).fadeIn("slow");
});
console.log("one runs");
}
function customFadeOut () {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).not(document.getElementById('card-6')).fadeOut("slow" , function () {
$("#card-6").delay(1000).rotate({angle:0});
});
});
console.log("two runs");
}
I want the customFadeOut runs only after customFadeIn is done, therefore I call it by this
customFadeIn();
customFadeOut();
But it did not work, I think I did something wrong here, a help would be really helpful.
You can make usage of jQuerys Deferred / promise objects. Animations do also "inherit" those objects and you can apply jQuery.when() to shoot for multiple promises to finish.
There are several ways to re-structure your code for that, a simple implementation of this could look like:
(function() {
var promises = [ ];
function customFadeIn () {
$("img.imgKit").each(function(index) {
promises.push( $(this).delay(1000*index).fadeIn("slow").promise() );
});
}
function customFadeOut () {
jQuery.when.apply( null, promises ).done(function() {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).not(document.getElementById('card-6')).fadeOut("slow" , function () {
$("#card-6").delay(1000).rotate({angle:0});
});
});
console.log("two runs");
});
}
}());
If I did everything correct there, customFadeOut sets up a listener which waits for all animations / promises to finish, before it runs its own code. You don't even have to explicitly call the .promise() method at the end, jQuery applies some white magic to link that node with a promise internally for you.
Demo: http://jsfiddle.net/RGgr3/
Looks like I did everything correct ;)

Categories

Resources