Handle nested Interval in Javascript - javascript

Some issue here;
I've got an HTTP GET call, that runs in an interval every 3 seconds.
The call's response data is an array of objects, and I want to log them to the console.
How can I avoid collision between intervals?
An example:
setInterval(function(){
$http.get('/some/api').then(function(dataArray){
dataArray.forEach(function(element){
setInterval(function(){
console.log(element);
},1000)
});
})
},5000)
Assume that the arrays from the api are: [1,2,3] at the first time, and [4,5,6] at the second interval, I need to see:
1
2
3
4
5
6
Thanks

I would want to get away from this nested intervals, other approach what I can think of is to have a array that adds on the data into it from each $http.get('/some/api') call and I will have another function outside of this interval which purely works on the array variable. To be clear let see this sample.
var valuesToLog =[]; // holds the elements
setInterval(function(){
$http.get('/some/api').then(function(dataArray){
valuesToLog.push(dataArray); //keep adding into the array for logging
})
},5000)
//new player which runs every second.
setInterval(funcion(){
if(valuesToLog.length){
console.log(valuesToLog[0]); //log first item
valuesToLog.splice(0,1) // and remove the logged item. ie: first item
}
},1000);
So every second I log the first item in the array and remove it.. Also since the interval is never killed it checks the array every second and if items are present it will log..
Hope this is helpful.

Substitute setTimout() for setInterval(); multiply the index of .forEach() callback function by duration 1000
var dataArray = [1, 2, 3];
dataArray.forEach(function(element, index) {
setTimeout(function() {
console.log(element);
}, 1000 * index)
});
You can alternatively call clearInterval() at each successful response, use Promise.all(), Promise() constructor, substitute .map() for .forEach(), call setInterval at .then() chained to Promise.all() when all setTimeout() calls have completed within Promise() constructor
function delay() {
return $http.get('/some/api').then(function(dataArray) {
clearInterval(interval);
return Promise.all(dataArray.map(function(element, index) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log(element);
resolve();
}, 1000);
});
}))
.then(function() {
interval = setInterval(delay, 5000)
});
})
}
interval = setInterval(delay, 5000);
function get() {
return new Promise(function(resolve) {
resolve(Array.from(Array(Math.floor(Math.random() * 10)).keys()))
})
}
function delay() {
return get().then(function(dataArray) {
clearInterval(interval);
console.log("current `dataArray.length`:", dataArray.length);
return Promise.all(dataArray.map(function(element, index) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log(element);
resolve()
}, 1000 * index)
})
}))
.then(function() {
interval = setInterval(delay, 5000)
});
})
}
interval = setInterval(delay, 5000)

Related

Using JS Promise as an interrupt

I wanted to directly call a function (like interrupt handler) when a certain condition is met. I didn't want to using "polling" for that as it increases time complexity.
count = 1
p = new Promise((resolve, reject)=>{
if(count == 2){
resolve("hello")
}
});
p.then((msg)=>{
console.log(msg)
})
console.log("1 now");
count = 2;
I expected console.log(msg) to run when count=2 but this is not the case. It turned out that the promise is still "pending". What is the reason this happens? And how do I implement my question.
You can use a Proxy to listen variable changes.
const count = new Proxy({
value: 0
}, {
set(target, prop, val) {
// set value
target[prop] = val;
console.log(`count is ${val}`);
// stop condition
if (val == 2) {
console.log(`count is 2(trigger stop condition)`);
}
}
});
// wait 2 seconds and change count.value to 1
setTimeout(() => count.value = 1, 2000);
// wait 2 seconds and change count.value to 2
// it triggers the stop condition
setTimeout(() => count.value = 2, 2000);
console.log("Waiting for changes ...");
reference: Listen to js variable change
Proxy is one of the solutions for this. But I post another approach for your case.
You can define a custom class or object, and work with that class. Also you register your custom listener for it, and do whatever.
This is a sample of my code. Maybe it will give you some ideas for your solution.
class MyObject {
constructor(value, func) {
this._value = value;
this._listener = func;
}
get value() {
return this._value;
}
set value(newValue) {
this._listener(newValue);
this._value = newValue;
}
}
function customListener(changedValue) {
console.log(`New Value Detected: ${changedValue}`);
}
const count = new MyObject(1, customListener);
count.value = 2;
The issue you're having is that the code inside the Promise resolves synchronously. It seems like you are assuming Promises are by default async, but that is a common async misconception. So, the code
if(count == 2){
resolve("hello")
}
resolves synchronously (that is, right after you declare count to be 1) so the Promise will never be resolved. If you want to asynchronously check for a condition without using libraries, you can use setInterval:
function checkForCondition(count, time){
return new Promise(resolve => {
const interval = setInterval(() => {
if (count == 2){
resolve("The count is 2!");
}
} , time);
});
}
If you call this function, the callback inside setInterval will be placed on the event loop every x ms, where x is equal to the time parameter.

Loop iteration with time interval before passing value to included function

I'm trying to figure out how to set time out for function inside the loop iteration in Ionic TypeScript application.
setInterval makes equal time interval with calling the function in endless repetition:
setInterval(() => {
this.myFunc1(val);
}, 800);
setTimeout gives required result if listed sequentially:
setTimeout(() => {
this.myFunc1(val);
}, 800);
setTimeout(() => {
this.myFunc1(val);
}, 1200);
but how to loop with time interval trough the updated list and wait while pass second value val to the function, or call myFunc1 when it will be finished in previous iteration:
async myFunc2() {
for (let val of this.myValueList) {
/// wait for 5 sec or wait for finishing process, then pass value calling function:
this.myFunc1(val);
}
}
setInterval is the correct choice here. What's missing is that you need to clear the interval. setInterval returns an id that you can pass to clearInterval which stops the iteration.
Here I'm passing data to console.log, waiting a second, then repeating till done.
const myValueList = [5,6,7,8,9,10];
let i = 0;
const id = setInterval(() => {
console.log(myValueList[i++]);
if (i === myValueList.length) {
clearInterval(id);
console.log("done!");
}
}, 1000);
Simplest solution for waiting approximately 5 seconds:
const wait = t => new Promise(r => setTimeout(r, t));
and then in your code you can just do:
async myFunc2() {
for (let val of this.myValueList) {
await wait(5000);
this.myFunc1(val);
}
}
If myFunc1 is async, and you just want to wait for it to finish executing before continuing the loop, then you would just do:
async myFunc2() {
for (let val of this.myValueList) {
await this.myFunc1(val);
}
}

JS: Get inner function arguments in asynchronous functions and execute callback

I try to write the function that returns all results of asynchronous functions and execute a callback that push into an array and log the result of every async function.
As a waiter that brings all dishes when they are all done.
I don't understand how to get the child arguments that should be returned as a result. The code of task and my not working solution is below:
The task:
var dishOne = function(child) {
setTimeout(function() {
child('soup');
}, 1000);
};
var dishTwo = function(child) {
setTimeout(function() {
child('dessert');
}, 1500);
};
waiter([dishOne, dishTwo], function(results) {
console.log(results); // console output = ['soup', 'dessert']
});
My not working solution:
function child(arg) {
this.arr.push(arg)
}
function waiter(funcArray, doneAll) {
var result = {
arr: []
};
let i = 0;
const x = child.bind(result)
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
}
Problem is this part:
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
which is a synchronous function so when you check if(i == 2), you basically check, that you have called all async functions, but they did not returned anything yet, so all you know is, that the functions have been called, but result.arr is not yet populated.
You must move the doneAll(result.arr) expression into child callback, then it will be called by async function as it returns result.
Simpliest solution I can think of is writing your child as
function child(arg) {
if (this.arr.push(arg) === this.allCount) this.doneAll(this.arr);
}
and in your waiter function enhance result object
var result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
This shall work, but has one drawback -- position of results does not keep position of functions in funcArray, the position of results is sorted by duration of async function, simply the first resolved would take first result etc. If this is a problem, you must pass also index to your child function to store result at precious position in result array and then the check by arr.length would not work, because JS array returns length as the highest index + 1, so if your last funcArray would fulfill first, it'll fill last index and the length of result.arr will be equal to this.allCount, so for keeping order of result the same as funcArray, you will need to store number of returned results as another number, increase that number with every new result and compare that number to allCount.
Or decrease allCount like so
function child(idx, arg) {
this.arr[idx] = arg;
if (--this.allCount === 0) this.doneAll(this.arr);
}
And modify your waiter function
function waiter(funcArray, doneAll) {
const result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
funcArray.forEach(function(f, i) {
f(child.bind(result, i));
});
}
Why not Promise?
function dishOne() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('soup') }, 1000)
})
}
function dishTwo() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('dessert') }, 1500)
})
}
Your waiter function:
function waiter(dishes, callback) {
return Promise.all(dishes).then(callback)
}
And you can use it like this
waiter([dishOne(), dishTwo()], function(results) {
// Invoked when all dishes are done
console.log(results) // ['soup', dessert']
})
Much easier to understand. Right?

Create a delay within a for each loop

I want to be able to make the console.log(key); make the log every 5 seconds, at the moment there is no delay and all the data gets logged at the same time.
//the location of my usres table
var ref = new Firebase('https://myapp.firebaseIO.com/users/');
//ask fire base to get the records just once
ref.once("value", function (snapshot) {
//loop through the retuned records
snapshot.forEach(function (childSnapshot) {
// get just the value for the key
var key = childSnapshot.key();
// log the key - this will be changed to send the value to another function
console.log(key);
});
})
The console.log above give me thousands of Id's, I need to pass these Id's to another function, If I pass all these id's all at one time the next function will blow up, so I want to pass them slowly, one by one to the next function. other ideas welcomed.
Use closure to get the value of key of current iteration after the setTimeout
Try this:
var ref = new Firebase('https://myapp.firebaseIO.com/users/');
ref.once("value", function(snapshot) {
snapshot.forEach(function(childSnapshot, index) {
var key = childSnapshot.key();
setTimeout((function(key) {
return function() {
console.log(key);
}
})(key), 5000 * index);
});
});
var ref = new Firebase('https://myapp.firebaseIO.com/users/');
ref.once("value", function (snapshot) {
for(var i=0; i<snapshot.lenght;i++){
var childSnapshot = snapshot[i];
var key = childSnapshot.key();
setTimeout(function () {
console.log(key);
}, 5000); // will stop the loop for 5 seconds every time
}
});
Maybe you mean a custom array iterator that iterates to the next element in the array after a specified delay. I made one using functional programming principals to promote re usability. see below
/*
* makeTimedForEach
*
* this will create an array itererator that will loop through an array
* at a specified timeout delay.
*
* #param {array} arr array to iterate
*
* #returns {Function} function to pass in callback and delay
*/
function makeTimedForEach(arr) {
return function timedForEach(fn, delay) {
var ii = 0;
function iterate() {
if (ii < arr.length) {
// call the callback injecting the current element, index and array like native forEach
fn.call( arr[ii], arr[ii], ii, arr );
ii++;
// call iterate again
setTimeout(iterate, delay);
}
}
// call for the first time you could add a setTimout here
// if you needed a delay at the start
// setTimeout( iterate, delay );
iterate();
}
}
// Snapshot mock
function Snapshot(key) {
this._key = key;
}
Snapshot.prototype = {
key: function(){
return this._key;
}
}
var
// a mock of the snapshot you get returned from `new FireBase`
snapshot = [new Snapshot('one'), new Snapshot('two'), new Snapshot('three')],
// create the iterator
snapshotTimedForEach = makeTimedForEach(snapshot);
// call the iterator passing in the callback and the delay time
snapshotTimedForEach(function(childSnapshot, ii, arr) {
console.log( childSnapshot.key(), childSnapshot, ii, arr );
}, 1000);
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
original answer:
Im not sure if this is what you need, I cant see a point in logging the same result every 5 seconds. It seems you may want to poll the firebase api every 5 seconds
var ref = new Firebase('https://myapp.firebaseIO.com/users/');
ref.once("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key();
// you can use setTimeout that recursively calls itself,
// setTimeout will run the callback after the time has passed
// and wont lock up your thread like setInterval
function log() {
console.log(key);
setTimeout( log, 5000);
}
// call log for the first time
setTimeout( log, 5000);
});
})
You can use setTimeout and recursive function like this:
function values(obj) {
if (obj instanceof Array) {
return obj.slice();
} else {
return Object.keys(obj).map(function(key) {
return obj[key];
});
}
}
ref.once("value", function (snapshot) {
var array = values(snapshot.val());
(function process() {
var item = array.shift();
if (item) {
var key = item.key();
console.log(key);
setTimeout(process, 5000);
}
})();
});

setTimeout in Node.js loop

I'm a bit confused as to how setTimeout works. I'm trying to have a setTimeout in a loop, so that the loop iterations are, say, 1s apart.
Each loop iteration makes an HTTP request and it seems like the server on the other end can't handle that many requests in such a short time span.
for (var i = 1; i<=2000 && ok; i++) {
var options = {
host:'www.host.com',
path:'/path/'+i
};
setTimeout(makeRequest(options, i), 1000);
};
Why does this not work and how can I achieve this?
Thank you
setTimeout is non blocking, it is asynchronous. You give it a callback and when the delay is over, your callback is called.
Here are some implementations:
Using recursion
You can use a recursive call in the setTimeout callback.
function waitAndDo(times) {
if(times < 1) {
return;
}
setTimeout(function() {
// Do something here
console.log('Doing a request');
waitAndDo(times-1);
}, 1000);
}
Here is how to use your function:
waitAndDo(2000); // Do it 2000 times
About stack overflow errors: setTimeout clear the call stack (see this question) so you don't have to worry about stack overflow on setTimeout recursive calls.
Using generators (io.js, ES6)
If you are already using io.js (the "next" Node.js that uses ES6) you can solve your problem without recursion with an elegant solution:
function* waitAndDo(times) {
for(var i=0; i<times; i++) {
// Sleep
yield function(callback) {
setTimeout(callback, 1000);
}
// Do something here
console.log('Doing a request');
}
}
Here is how to use your function (with co):
var co = require('co');
co(function* () {
yield waitAndDo(10);
});
BTW: This is really using a loop ;)
Generator functions documentation.
You need something like this
var counter = 5;
function makeRequst(options, i) {
// do your request here
}
function myFunction() {
alert(counter);
// create options object here
//var options = {
// host:'www.host.com',
// path:'/path/'+counter
//};
//makeRequest(options, counter);
counter--;
if (counter > 0) {
setTimeout(myFunction, 1000);
}
}
See also this fiddle
At the point of the alert(count); you can do your call to the server.
Note that the counter works opposite (counting down). I updated with some
comments where to do your thing.
Right now you're scheduling all of your requests to happen at the same time, just a second after the script runs. You'll need to do something like the following:
var numRequests = 2000,
cur = 1;
function scheduleRequest() {
if (cur > numRequests) return;
makeRequest({
host: 'www.host.com',
path: '/path/' + cur
}, cur);
cur++;
setTimeout(scheduleRequest, 1000)
}
Note that each subsequent request is only scheduled after the current one completes.
I might be late at the party but here is another (more readable) solution without the need to omit for loop.
What your code does is creating 2000 (actually 1999) setTimeout objects that will call the makeRequest function after 1 second from now. See, none of them knows about the existence of the other setTimeouts.
If you want them 1 sec apart from each other, you are responsible for creating them so.
This can be achieve by using your counter (in this case i) and the timeout delay.
for (var i = 1; i<=2000 && ok; i++) {
var options = {
host:'www.host.com',
path:'/path/'+i
};
setTimeout(makeRequest(options, i), i * 1000); //Note i * 1000
};
The first timeout object will be set for 1 second from now and the second one will be set for 2 seconds from now and so on; Meaning 1 second apart from each other.
I'm surprised that no one has mentioned this above, but it sounds like you need setInterval not setTimeout.
vat poller = setInterval(makeRequestFunc, 3000)
The code above will make a request every 3 seconds. Since you saved the object to the variable poller, you can stop polling by clearing the object like so:
cleanInterval(poller)
You're calling makeRequest() in your setTimeout call - you should be passing the function to setTimeout, not calling it, so something like
setTimeout(makeRequest, 1000);
without the ()
let i = 20;
let p = Promise.resolve(i)
while (i > 0) {
(i => {
p = p.then(() => {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(i);
resolve()
}, 2000)
})
})
})(i)
i--
}
p = p.then(data => console.log('execution ends'))
I'm very late on the subject (as usual ...;) but the only way I found to loop requests to a slow time response API and getting responses without HTTP 504 is using promises.
async function LoadDataFromAPI(myParametersArray) {
for(var i = 0; i < myParametersArray.length; i++) {
var x = await RunOneRequest(myParametersArray[i]);
console.log(x); // ok
}
}
The function called by the async function :
function RunOneRequest(parameter) {
return new Promise(resolve => {
setTimeout(() => {
request(parameter, (error, response, body) => {
// your request
});
resolve('ok);
}, 2000); // 2 secs
});
}

Categories

Resources