I have a function as below:
function foo(args1, args2, retry)
{
if (retry <= 0)
return false;
var isDone = callAnotherFunction(args1, args2);
if(!isDone) {
setInterval(function () {
foo(args1, args2, retry-1);
},
2000);
}
else
return true;
}
So I am not sure if the above implementation is correct. But I need to use this function in another function. And use the above function in an if block to decide if the other statement needs to be executed. Below is the usage of the above function.
function useIt(args1, args2)
{
// Other code
let store = function() {
if(!foo(args1, args2, 5)) {
cleanStorage(args1, args2);
return;
}
}
So the problem is in function useIt(), cleanStorage() does not wait for foo() to be executed if I am using setInterval or setTimeOut. So how do I need to implement the function foo() ? Kindly help me.
consider using promises
foo can be rewritten like this (I've replace setInterval with setTimeout):
function foo(args1, args2, retry) {
return new Promise(function (resolve, reject) {
if (retry <= 0)
reject();
var isDone = callAnotherFunction(args1, args2);
if (!isDone) {
setTimeout(function () {
resolve(foo(args1, args2, retry - 1));
}, 2000);
}
else
resolve(true);
})
}
and then use it like this:
function useIt(args1, args2) {
// Other code
let store = function () {
foo(args1, args2, 5).then(function () {
cleanStorage(args1, args2);
});
}
}
You should use Promises to do this
Something like this:
function foo(args1, args2, retry)
{
return new Promise(function(resolve, reject) {
if (retry <= 0)
reject();
var isDone = callAnotherFunction(args1, args2);
if(!isDone) {
setInterval(function () {
retry = retry - 1;
isDone = callAnotherFunction(args1, args2);
if (isDone)
resolve();
},
2000);
}
else
resolve();
}
}
function useIt(args1, args2)
{
// Other code
let store = function() {
foo(args1, args2, 5).then(result => {
cleanStorage(args1, args2);
return;
}
}
}
Related
I am trying to make a timer, and trying to exit from the callback hell that is happening inside the code I am writing, this is a part from the code I have written, the problem is that I am not receiving the feedback from the promise, so no resolve message, no reject message.
what is the problem?
var timer = 15;
var startTimer = (timer) => {
return new Promise((resolve, reject)=>{
if(resolve){
var countDown = setInterval(() => {
console.log(timer);
timer--;
if (timer == 0) {
clearInterval(countDown);
return "YOU ARE LOSER";
}
}, 1000);
}
if(reject){
return "sorry something went wrong!";
}
})
}
startTimer(timer)
.then(message =>{
console.log(message);
//the message should be "You are loser!".
})
.catch(message =>{
console.log(message);
})
resolve and reject are functions to call. resolve is for when the asynchronous operation has completed, and reject for when an error has occurred. When invoking these functions, you can supply a value to propagate along the promise chain.
var count = 5
var startTimer = (count)=>{
return new Promise((resolve,reject)=>{
try {
var intervalId = setInterval(()=>{
if(count) {
console.log(count--)
return
}
clearInterval(intervalId)
resolve( "YOU ARE LOSER")
}, 1000)
} catch {
reject('sorry something went wrong!')
}
})
}
startTimer(count)
.then((result)=>{ console.log(result) })
.catch((err)=>{ console.log(err) })
You're using promises wrong, take a look at this friendly tutorial.
You resolve or reject the value that you want to return.
var timer = 15;
var startTimer = (timer) => {
return new Promise((resolve, reject) => {
try {
var countDown = setInterval(() => {
console.log(timer);
timer--;
if (timer == 0) {
clearInterval(countDown);
resolve("YOU ARE LOSER");
}
}, 1000);
} catch (err) {
reject("sorry something went wrong!");
}
})
}
startTimer(timer)
.then(message => {
console.log(message);
})
.catch(message => {
console.log(message);
})
Simplified version without promises
var timer = 10;
var countDown = 0;
var startTimer = fn => setInterval(() => {
console.log(timer);
if (--timer === 0)
{
clearInterval(countDown);
console.log("You lose");
if(fn)
fn();
}
}, 500);
function onTimerFinished() {
console.log("Timer has finished");
}
try {
countDown = startTimer(onTimerFinished);
} catch (err) {
console.error(err);
}
for example, if I need to do task s1,s2,s3 linerly,it would be look like this:
var s1=function(){
document.write("[1s task]");
setTimeout(s2,2000);
}
var s2=function(){
document.write("[2s task]");
setTimeout(s3,3000);
}
var s3=function(){
document.write("[3s task]");
}
setTimeout(s1,1000);
but it is very hard to maintain if I want to change the order from s1,s2,s3 to s3,s1,s2. How to wrap and 'objectize' a task which look like this:
mySetTimeout(new MyTask(s1,1000),new MyTask(s2,1000),new MyTask(s3,1000));
so that it is easy to change order from s1,s2,s3 to s3,s1,s2:
mySetTimeout(new MyTask(s3,3000),new MyTask(s1,1000),new MyTask(s2,2000));
? How to write mySetTimeout and MyTask?
The solution you didn't know you were looking for are promises. They're objects representing asynchronous results, and can be chained with callback functions.
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
function s1() {
console.log("[1s task]");
return delay(1000);
}
function s2() {
console.log("[2s task]");
return delay(2000);
}
function s3() {
console.log("[3s task]");
return delay(3000);
}
s1().then(s2).then(s3);
Create a schedule task which takes different tasks as parameters (an example below)
function scheduleTask()
{
var args = [...arguments];
var firstTask = args.slice(0,1)[0];
args = args.slice(1);
//console.log(args, firstTask);
firstTask && setTimeout( function(){
firstTask( function(){
scheduleTask(...args);
});
}, 1000); //timeout is constant
}
scheduleTask(s1, s2, s3);
Demo
function scheduleTask()
{
var args = [...arguments];
var firstTask = args.slice(0,1)[0];
args = args.slice(1);
//console.log(args, firstTask);
firstTask && setTimeout( function(){
firstTask( function(){
scheduleTask(...args);
});
}, 1000); //timeout is constant
}
scheduleTask(s1, s2, s3);
function s1( cb )
{
console.log("s1");
cb();
}
function s2( cb )
{
console.log("s2");
cb();
}
function s3( cb )
{
console.log("s3");
cb();
}
Or as suggested by #Thomas you can shorten it by doing
function scheduleTask(firstTask, ...args)
{
firstTask && setTimeout(function() {
firstTask(function() {
scheduleTask(...args);
});
}, 1000); //timeout is constant
}
Demo
function scheduleTask(firstTask, ...args)
{
firstTask && setTimeout(function() {
firstTask(function() {
scheduleTask(...args);
});
}, 1000); //timeout is constant
}
scheduleTask(s1, s2, s3);
function s1(cb) {
console.log("s1");
cb();
}
function s2(cb) {
console.log("s2");
cb();
}
function s3(cb) {
console.log("s3");
cb();
}
I guess #Bergi's promise way is nice but you may still sequence your jobs without using promises. Just put your jobs in an array in the order you like them get processed and use a recursive sequencer.
var seqJobs = ([j,...js]) => j && setTimeout(_ => (j.job(), seqJobs(js)), j.dly),
jobs = [{job: _ => console.log("[0s task]"), dly: 500},
{job: _ => console.log("[1s task]"), dly: 2000},
{job: _ => console.log("[2s task]"), dly: 3000},
{job: _ => console.log("[3s task]"), dly: 1000}];
seqJobs(jobs);
I am load HTML (external app) into an iFrame
I want to "do" something (callback) when an element becomes available in my iFrame. Here how I wrote it, and I'd like to write this with Promises instead:
function doWhenAvailable(selector, callback) {
console.warn("doWhenAvailable", selector)
if ($('#myiFrame').contents().find(selector).length) {
var elt = $('#myiFrame').contents().find(selector);
console.info("doWhenAvailable Found", elt)
callback && callback(elt);
} else {
setTimeout(function() {
doWhenAvailable(selector, callback);
}, 1000);
}
}
Actually instead of using setTimeout, I'd like to use setInterval to repeat the "find element" until it's found and resolve the "promise".
No, you would not use setInterval, you just would wrap the timeout in a promise and drop the callback:
function wait(t) {
return new Promise(function(resolve) {
setTimeout(resolve, t);
});
}
function whenAvailable(selector) {
var elt = $('#myiFrame').contents().find(selector);
if (elt.length)
return Promise.resolve(elt);
else
return wait(1000).then(function() {
return whenAvailable(selector);
});
}
Keeping your recursive style, it would have become something like that :
function doWhenAvailable(selector) {
var dfd = jQuery.Deferred();
console.warn("doWhenAvailable", selector)
if ($('#myiFrame').contents().find(selector).length) {
var elt = $('#myiFrame').contents().find(selector);
console.info("doWhenAvailable Found", elt)
return dfd.resolve(elt);
} else {
setTimeout(function() {
doWhenAvailable(selector).then(function(e) {
dfd.resolve(e);
});
}, config[env].wrapper.timeOutInMs);
}
return dfd.promise();
}
But I would have tried to avoid recursive calls here
The general idea is to return a promise instead of receiving a callback.
Example:
var xpto = function(res) {
return new Promise((resolve, reject) => {
if(res > 0) resolve('Is greater');
else reject(new Error('is lower'));
});
}
So in your case:
function doWhenAvailable(selector) {
function work(callback) {
if ($('#myiFrame').contents().find(selector).length) {
var elt = $('#myiFrame').contents().find(selector);
console.info("doWhenAvailable Found", elt)
callback(elt);
}
}
return new Promise((resolve, reject) => {
console.warn("doWhenAvailable", selector)
setInterval(() => work(resolve), 1000);
})
}
Here:
function doWhenAvailable(selector) {
return new Promise(function(resolve, reject){
console.warn("doWhenAvailable", selector)
if ($('#myiFrame').contents().find(selector).length) {
var elt = $('#myiFrame').contents().find(selector);
console.info("doWhenAvailable Found", elt)
resolve(elt);
} else {
setTimeout(function() {
doWhenAvailable(selector).then(function(data){
resolve(data);
});
}, config[env].wrapper.timeOutInMs);
}
}
}
And call your function like that:
doWhenAvailable("#elemId").then(function(elt){
//do what you want
});
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>
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