How do I do a callback chain with q? - javascript

I some problems understanding how to use "q" (https://github.com/kriskowal/q) a promises library for javascript:
var delayOne = function() {
setTimeout(function() {
return 'hi';
}, 100);
};
var delayTwo = function(preValue) {
setTimeout(function() {
return preValue + ' my name';
}, 200);
};
var delayThree = function(preValue) {
setTimeout(function() {
return preValue + ' is bodo';
}, 300);
};
var delayFour = function(preValue) {
setTimeout(function() {
console.log(preValue);
}, 400);
};
Q.fcall(delayOne).then(delayTwo).then(delayThree).then(delayFour).end();
this only returns undefined...

As wroniasty pointed out, you need to return a promise from each of those functions, but you should also abstract any callback oriented APIs (like setTimeout) as much as possible and use APIs that return promises instead.
In the case of setTimeout, Q already provides Q.delay(ms) which returns a promise that will be resolved after the specified number of milliseconds, perfect for replacing setTimeout:
var delayOne = function() {
return Q.delay(100).then(function() {
return 'hi';
});
};
var delayTwo = function(preValue) {
return Q.delay(200).then(function() {
return preValue + ' my name';
});
};
var delayThree = function(preValue) {
return Q.delay(300).then(function() {
return preValue + ' is bodo';
});
};
var delayFour = function(preValue) {
return Q.delay(400).then(function() {
console.log(preValue);
});
};
Q.fcall(delayOne).then(delayTwo).then(delayThree).then(delayFour).done();
(note: end has been replaced with done)

The reason you get "undefined" is because the functions you are chaining are not returning anything:
var delayOne = function() {
setTimeout(function() {
return 'hi';
}, 100);
};
delayOne calls setTimeout, and returns nothing (undefined).
To achieve your goal you must use Q.defer:
var delayOne = function() {
var d = Q.defer();
setTimeout(function() {
d.resolve("HELLO");
}, 100);
return d.promise;
};
var delayTwo = function(preValue) {
setTimeout(function() {
alert(preValue);
},
400);
};
delayOne().then ( delayTwo );
http://jsfiddle.net/uzJrs/2/

Related

jquery deferred and promise in a loop

Here is something wrong. All functions should be called synchronously. Could anyone give me a hint? I think that is an error in the for loop
Here is my code:
var radioValues = msg.options;
var cn = gotoReport()
.then(clickReport)
.then(clickReportFake)
.then(clickNext);
for (var i = 0; i < radioValues.length; i++){
cn = cn.then(clickOption(radioValues[i])).then(clickNext);
}
cn.then(clickSendToFacebook).then(clickFinish);
//all called functions look like that
function clickNext(){
return process(function(){
console.log("clickNext");
var next = $('button:contains("Weiter")');
$(next).click();
},3000);
}
function process(action, ms) {
var deferred = $.Deferred();
timer = setInterval(function() {
deferred.notify();
}, 1000);
setTimeout(function() {
clearInterval(timer);
action();
deferred.resolve();
}, ms);
// return deferred;
return deferred.promise();
}
function sleep(ms)
{
return(new Promise(function(resolve, reject) {
setTimeout(function() { resolve(); }, ms);
}));
}
Here is the output
gotoReport
clickOption=option3
clickReport
clickReportFake
clickNext
clickNext
clickSendToFacebook
clickFinish
One major issue is here:
cn.then(clickOption(radioValues[i]))
You are not passing the clickOption function as argument to then -- you are invoking it. Instead do:
cn.then(clickOption.bind(null, radioValues[i]))

Identify if 2 AJAX process is done even if remaining AJAX Processes is still on progress

I know already how to detect if all AJAX Processes is done by the code below:
jQuery(document).ajaxStop(function(){
alert('All AJAX Process is done.');
});
For example there are three AJAX Process: oneProcess, twoProcess, threeProcess.
oneProcess and twoProcess are already finished and threeProcess is still processing. How I invoked that the two AJAX process is done?
The simplest approach would be to utilize $.when(), passing oneProcess, twoProcess as parameters.
var oneProcess = new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("oneProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
}).then(log);
var twoProcess = new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("twoProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
}).then(log);
var threeProcess = function(msg) {
log(msg);
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("threeProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
})
}
function log(msg) {
console.log(msg);
}
var checkOneTwo = $.when(oneProcess, twoProcess);
checkOneTwo.then(function(one, two) {
threeProcess(
"oneProcess state:" + oneProcess.state()
+ ", twoProcess state:" + twoProcess.state()
).then(log)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
Alternatively you can utilize setInterval to call .state() on each deferred object to check for both oneProcess, twoProcess deferred.state() calls to return "resolved".
var oneProcess = new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("oneProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
}).then(log);
var twoProcess = new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("twoProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
}).then(log);
var threeProcess = function(msg) {
log(msg);
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve("threeProcess complete")
}, Math.floor(Math.random() * 1500));
return dfd.promise()
})
}
function log(msg) {
console.log(msg);
}
var interval = null;
var checkOneTwo = function() {
var oneTwo = new $.Deferred();
interval = setInterval(function() {
console.log(oneProcess.state() === "resolved"
, twoProcess.state() === "resolved");
if (oneProcess.state() === "resolved" &&
twoProcess.state() === "resolved") {
clearInterval(interval);
oneTwo.resolve()
}
}, 100);
return oneTwo.promise().then(function() {
return threeProcess(
`oneProcess state:${oneProcess.state()}`
+ `twoProcess state:${twoProcess.state()}`
)
})
}
checkOneTwo().then(log);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>

How to run a function after two async functions complete

Say I have an array of functions that invoke a setTimeout.
[
function(cb){
setTimeout(function(){
cb('one');
}, 200);
},
function(cb){
setTimeout(function(){
cb('two');
}, 100);
}
]
Is there a way to access the time parameter (200, 100) and save the sum of that to a variable?
I want to execute a function only when both of those functions are done
A better approach is to use promises and Promise.all:
var task1 = new Promise(function(resolve,reject) {
setTimeout(function() {
//do something
resolve();
}, 100);
});
var task2 = new Promise(function(resolve,reject) {
setTimeout(function() {
//do something
resolve();
}, 200);
});
Promise.all([task1, task2]).then(function() {
//will be executed when both complete
});
You can mimic it with a closure for the count.
function out(s) {
var node = document.createElement('div');
node.innerHTML = s + '<br>';
document.getElementById('out').appendChild(node);
}
var f = [
function (cb) { setTimeout(function () { cb('one'); }, 100); },
function (cb) { setTimeout(function () { cb('two'); }, 200); }
],
useCounter = function () {
var count = 2;
return function (s) {
count--;
out(s + ' ' + count);
!count && out('done');
}
}();
f[0](useCounter);
f[1](useCounter);
<div id="out"></div>

Execute function queue in javascript

I'm trying to create a function queue with several functions in it.
After the creation i want to execute each function in it's turn.
But these function have delayed instructions inside of them, so i want to wait for each of the functions to complete its execution before the continuing.
My attempts:
var funqueue = [];
funqueue.push( function() {fun1() });
funqueue.push( function() {fun2() });
funqueue.push( function() {fun3() });
executeFunctionQueue(funqueue);
Where the execute function is:
function executeFunctionQueue(funqueue){
var fun1=funqueue.pop;
$.when(fun1()).then(executeFunctionQueue(funqueue));
}
But this does not work.
How should i do it?
Try utilizing .queue() , .promise() ; see also Change easing functions on animations in jQuery queue
function fun1() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(1)
}, 1500)
}).promise().then(msg)
}
function fun2() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(2)
}, 1500)
}).promise().then(msg)
}
function fun3() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(3)
}, 1500)
}).promise().then(msg)
}
var funqueue = [];
funqueue.push(function() {
return fun1()
});
funqueue.push(function() {
return fun2()
});
funqueue.push(function() {
return fun3()
});
function msg(data) {
if (data === "complete") console.log(data)
else $("body").append(data + "<br>")
}
function executeFunctionQueue(funqueue) {
var deferred = funqueue.pop();
return deferred().then(function() {
// set `this` within `$.queue()` , `.then()` to empty object `{}`,
// or other object
return $({}).queue("fun", $.map(funqueue, function(fn) {
return function(next) {
// return `next` function in `"fun"` queue
return fn().then(next)
}
})).dequeue("fun").promise("fun")
.then(function() {
// return "complete" string when `fun` queue empty
return "complete"
})
});
}
executeFunctionQueue(funqueue)
.then(msg);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
Alternatively , using $.when()
function executeFunctionQueue(funqueue) {
return $.when(!!funqueue[funqueue.length - 1]
? funqueue.pop().call().then(function() {
return executeFunctionQueue(funqueue)})
: "complete")
}
executeFunctionQueue(funqueue)
.then(function(complete) {
console.log(complete)
});
function fun1() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(1)
}, 1500)
}).promise().then(msg)
}
function fun2() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(2)
}, 1500)
}).promise().then(msg)
}
function fun3() {
return $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(3)
}, 1500)
}).promise().then(msg)
}
var funqueue = [];
funqueue.push(function() {
return fun1()
});
funqueue.push(function() {
return fun2()
});
funqueue.push(function() {
return fun3()
});
function msg(data) {
if (data === "complete") console.log(data)
else $("body").append(data + "<br>")
}
function executeFunctionQueue(funqueue) {
return $.when(!!funqueue[funqueue.length - 1]
? funqueue.pop().call().then(function() {
return executeFunctionQueue(funqueue)})
: "complete")
}
executeFunctionQueue(funqueue)
.then(msg);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
If you have functions that return Promises, this can be done very simply with a function like sequence:
// sequence :: [(undefined -> Promise<undefined>)] -> Promise<undefined>
function sequence(fns) {
var fn = fns.shift();
return fn ? fn().then(sequence.bind(null, fns)) : Promise.resolve(undefined);
}
sequence assumes that your asynchronous/Promise-returning functions do not take any inputs and do not produce any outputs (that they are merely being called for side-effects.)
An example usage of the sequence function is:
sequence([f1, f2, f3]);
function f1() {
return new Promise(function (res) {
setTimeout(function () {
console.log('f1');
res();
}, 100);
});
}
function f2() {
return new Promise(function (res) {
setTimeout(function () {
console.log('f2');
res();
}, 1100);
});
}
function f3() {
return new Promise(function (res) {
setTimeout(function () {
console.log('f3');
res();
}, 10);
});
}
This will log out 'f1', 'f2', and 'f3' in order with the varying, specified time delays in between.
Use this
function executeFunctionQueue(funqueue){
if(!funqueue.length){
return
}
var fun1=funqueue.pop();
$.when(fun1()).then(function(){
executeFunctionQueue(funqueue)
});
}
Or even this if queued functions are not asynchronous.
function executeFunctionQueue(funqueue){
var fun=funqueue.pop();
fun()
if(!funqueue.length){
return
}
executeFunctionQueue(funqueue);
}
First create an array of functions as given:
var array_of_functions = [function1, function2, function3, function4];
When you want to execute a given function in the array try this:
array_of_functions[index]('mystring');
use deferred/promise pattern to execute functions on other function complete.
var createQueue = function () {
var d = $.Deferred(),
p = d.promise(),
triggerQueue = function () {
d.resolve();
};
return {
addToQueue: p.then,
triggerQueue: triggerQueue
}
};
var cq = createQueue();
cq.addToQueue(function () {
console.log("hi");
}).then(function () {
console.log("hello");
});
cq.triggerQueue();
In order to make a clean queue, your asynchronous functions will need to somehow signify when they are done, or the next function won't know when to begin. This means you cannot pass in just any old function; they'll need to follow some format. I'd suggest taking a done callback as the first parameter in your function calls. This way, you can support both synchronous and asynchronous functions.
var processQueue = function nextStep(queue) {
var next = queue.shift();
next && next(function() { nextStep(queue); });
}
function fun1(done) {
setTimeout(function() {
console.info('1');
done();
}, 1000);
}
function fun2(done) {
console.info('2');
done();
}
processQueue([fun1, fun2]);
// >> 1 second wait
// >> 1
// >> 2

Excecute JavaScript function after previous one completes

I want to execute several JavaScript functions in a specific order (like below) and not until the previous function has completed. I have tried this so many ways. Any suggestions? Any help is so greatly appreciated, I have been stuck on this for so long. Thanks in advance!
function savedSearch(e){
applySearch1("ColumnA");
applySearch2("ColumnB");
applySearch3("ColumnC");
applySearch4("ColumnD");
applySearch5("ColumnE");
applySearch6("ColumnF");
}
To add in response to the other answer by Mohkhan, you can also use the async library.
https://github.com/caolan/async
That will keep you out of callback hell and make for a much easier to read list of functions.
You should use callbacks in all your applySearch* functions.
Like this.
function savedSearch(e){
applySearch1("ColumnA", function(){
applySearch2("ColumnB", function(){
applySearch3("ColumnC", function(){
applySearch4("ColumnD", function(){
applySearch5("ColumnE",function(){
applySearch6("ColumnF", function(){
// You are done
});
});
});
});
});
});
}
If use jquery, it has deferred objects which helps you deal with async functions.
Here is an example:
// Code goes here
$(document).ready(function() {
function Pipe() {
this.asyncTasks = [];
this.observers = {};
this.on = function(eventName, fn) {
if (!this.observers[eventName]) {
this.observers[eventName] = $.Callbacks;
}
this.observers[eventName].add(fn);
}
this.fire = function(eventName, data) {
if (this.observers[eventName]) {
this.observers[eventName].fire(data);
}
}
this.register = function(asyncFn, context) {
this.asyncTasks.push(new Task(asyncFn, context));
}
this.start = function() {
this.fire('start');
this._next();
}
this._next = function() {
var task = this.asyncTasks.shift();
if (task) {
task.execute().then($.proxy(this._next, this));
} else {
this.fire('end');
}
}
var Task = function(fn, context) {
this.fn = fn;
this.context = context;
this.execute = function() {
if (!this.fn) {
throw new Exception("Failed to execute.");
}
var promise = this.fn.call(context);
this.fn = null;
return promise;
}
}
}
var bookMoview = function() {
var dfd = jQuery.Deferred();
// Resolve after a random interval
setTimeout(function() {
dfd.resolve("The movie is booked");
console.log("The movie is booked");
}, Math.floor(400 + Math.random() * 2000));
// Return the Promise so caller can't change the Deferred
return dfd.promise();
}
var bookTaxi = function() {
var dfd = jQuery.Deferred();
// Resolve after a random interval
setTimeout(function() {
console.log("Taxi is booked");
dfd.resolve("Taxi is booked");
}, Math.floor(400 + Math.random() * 2000));
// Return the Promise so caller can't change the Deferred
return dfd.promise();
}
var pipe = new Pipe();
pipe.register(bookMoview);
pipe.register(bookTaxi);
pipe.start();
});

Categories