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);
}
})();
});
Related
I am writing a recursive function inside for loop like below:
var output = [];
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
for (var i=0; i < result.myKey.length; i++){
if(result.myKey[i].name === 'something'){
myFunc(result.myKey[i].recurseValue, function(recursiveResult){
//some recursive stuff
output.push(recursiveResult.someValue)
});
}
}
});
}
And initiating the recursive function like below:
myFunc(initialValue, function(result){
//some stuff
});
Its working fine, but how do I know when my recursive flow ends so that I can do something else from the final output?
You can use Promises™! It's basically a way to defer a callback till after an Asynchronous flow is completed: Example:
// Instead of passing your normal callback, we'll tell the
// function to use resolve(results) to pass your results to the
// next code block so it can do something after all your recursions are completed
const someTask = new Promise(resolve => myFunc(initialValue, resolve))
someTask.then(result => {
/* Do Something with the results at the end of aformentioned callback hell :D */
})
PS. You also have to modify your original function signature to:
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
const cbks = [] //Store the async resuls of all myFunc() executions
for (i=0; i < result.myKey.length; i++){
if(results[i] === 'something'){
cbks.push(new Promise(res => myFunc(result[i].recurseValue, res)))
}
}
//Run all async myFunc() and return the results in an array
Promise.all(cbks).then(callback)
});
}
function myFunc(resolve) {
var rec = function(myVal, cb) {
myOther(recurseValue, function(result) {
var hasName = result[myKey].filter(function(obj) {
return obj.name === 'something';
})[0];
if (hasName) {
rec(hasName[recurseValue], function(recResult) {
// other recursive stuff
});
} else {
resolve(?); // whatever the final value should be
}
});
};
return rec;
}
function recurseAsync(f, initial) {
return new Promise(function(resolve, reject) {
f(resolve)(initial);
});
}
Couple notes.
The recurseAsync function takes a function that takes a resolution callback and returns a recursive function that calls that callback when finished to resolve the promise. myFunc has been altered to fit that format.
I used array filtering rather than a for loop and shortened some names. Also if you are using a variable for object access use [] instead of .. To use the final value when all of this is finished you can call .then on the promise.
// whatever initial value 'foo' should be
var finished = recurseAsync(myFunc, foo);
finished.then(function(finalValue) {
// do something with the final result of all the recursion
});
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?
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)
I have a data set, over which I iterate and run a fairly heavy operation on each element. To prevent the browser from freezing, I've set a timeout of 0ms. While the browser doesn't freeze, the calculation can take a good few seconds depending on user input. Here's the code:
function calculation(){
$.each(data,function(key,value){
setTimeout(function(){
doTheHeavyLifting(value);
},0);
});
}
Now, the problem is, that I want to stop the previous calculation if the user requests to run the calculation again with new values.
I tried defining a continueCalculating boolean outside the function, and setting that to false in runCalculation() before calling calculation(), and inside calculation() resetting it back to true. Here's the modified code:
var continueCalculating=false;
function runCalculator(){
continueCalculating=false;
calculation();
}
function calculation(){
continueCalculating=true;
$.each(data,function(key,value){
setTimeout(function(){
if(continueCalculating){
doTheHeavyLifting(value);
}else{
console.log("Abort the mission!");
return false;
}
},0);
});
}
Well, that didn't do the trick, and in retrospect was a pretty silly idea anyway. So, next I tried also clearing all the timeouts. Here's the modified code:
var continueCalculating=false;
var operationQueue=[];
function runCalculator(){
continueCalculating=false;
$.each(operationQueue, function(k,v){
clearTimeout(v);
});
calculation();
}
function calculation(){
continueCalculating=true;
$.each(data,function(key,value){
operationQueue.push(setTimeout(function(){
if(continueCalculating){
doTheHeavyLifting(value);
}else{
console.log("Abort the mission!");
return false;
}
},0));
});
}
Well, that didn't yield any results either.
I know $.each is slower than for, but there are only about a couple dozen items to iterate over, so it certainly isn't the bottle neck here. I'm not looking for ways to optimize this code, I just need to stop an on-going calculation. Is this possible?
You should iterate asynchronously:
var data = [0,1,2,3,4,5,6,7,8,9,"end"];
function doTheHeavyLifting(value) {
console.log(value);
}
function calculation(data) {
var key = 0, timer;
(function loop() {
if(key < data.length) {
doTheHeavyLifting(data[key]);
key++;
timer = setTimeout(loop, 1e3);
}
})();
return {stop: function() { clearTimeout(timer) } };
}
var calc = calculation(data);
document.querySelector('#stop').onclick = calc.stop;
<button id="stop">Stop</button>
You can use $.queue(), $.Deferred(), .promise()
// check at `if` condition within deferred
var stop = false;
$("button").on("click", function() {
// clear `"calculation"` queue at `click` event at `button` element
$.queue(data, "calculation", []);
// set `stop` to `true` at `click` event at `button`
stop = true;
});
// `data`
var data = Array(100).fill(0).map((v, k) => k);
function doTheHeavyLifting(val) {
console.log(val);
// return `.promise()` when process within `doTheHeavyLifting` completes
return $("body").append("\n" + val).promise()
}
function calculation() {
$.queue(data, "calculation", $.map(data, function(value, index) {
return function(next) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
if (!stop) {
dfd.resolve(doTheHeavyLifting(value))
} else {
dfd.rejectWith(
$.queue(data, "calculation")
, ["queue stopped at " + index]
);
}
},0)
})
// iterate next item in `data`
.then(next)
// do stuff if `"calculation"` queue is empty array
.fail(function(message) {
console.log(message
, "calculation queue:", this);
alert(message);
})
}
}));
// start queue
$.dequeue(data, "calculation");
}
calculation();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<button>stop calculation</button>
I am having trouble with Javascript async call. The Car class below has a move function which takes two arguments, the first one is a value, the second one is a call back function, which will be called after 1 second, and this callback function takes the value returned by forward method.
var Car = function() {
this._count = 0;
};
Car.prototype = {
move: function(value, onMoved) {
setTimeout(function() {
onMoved(this.forward(value));
}.bind(this), 1000);
},
forward: function(value) {
this._count = this._count + value;
return this._count;
}
};
I want to call the move function like this:
var values = [1, 2, 3, 4];
var car = new Car();
values.forEach(function(value) {
car.move(value, function(result) {
console.log(result);
});
});
Now my problem is the call back function onMoved does not wait for 1 second to execute between each value that it is outputting. How can I make it so it wait between each value that it is outputting? I am allowed to use underscore.js. Thanks.
setTimeout in javascript registers callback function in a queue to execute in future once the current execution stack is free. for example:-
while(1){
setTimeout(function(){
console.log('hello');
},1000);
}
this will not print hello as the execution stack will never be free.
Coming back to the example we call move method which will be pushed to a queue. After one second it starts executing each function one by one without any delay as the setTimeout is set to a fixed time ie. 1000 millisecond.
workaround:-
var Car = function() {
this._count = 0;
};
Car.statCount = 0;
Car.prototype = {
move: function(value, onMoved) {
this.constructor.statCount++;
setTimeout(function() {
onMoved(this.forward(value));
}.bind(this), 1000*Car.statCount);
},
forward: function(value) {
this._count = this._count + value;
return this._count;
},
constructor: Car
};
var values = [1, 2, 3, 4];
var car = new Car();
values.forEach(function(value) {
car.move(value, function(result) {
console.log(result);
});
});
Since you want a 1 sec. wait between each call, you might consider using the setInterval method on window, instead of utilizing the setTimeout method:
You could add some method performing some actions for each iteration, and finish iterating when all values are processed:
var i=0;
function go(result) {
if (!!values[i]) {
car.move(values[i], function(result) {console.log(result);})
i++;
} else {
window.clearInterval(interval);
}
}
And call this function using setInterval
var interval = window.setInterval(go, 1000);
Have a look at the following fiddle:
https://jsfiddle.net/adw1k98m/1/
(see console log for output)