Underscore js times series - javascript

I'm using UnderscoreJs with nodejs and have a need for the _.times() method. times() will invoke a function X number of times
This works as expected, however I need to iterate in a series, instead of in parallel which this appears to be doing.
Any idea if there's a way to use this in series w/ callback methods?

Given something like this:
function f() {
some_async_call({ callback: function(err, results) {...})
}
_(3).times(f);
Then the three f calls will happen in series but the some_async_call calls won't necessarily happen in series because they're asynchronous.
If you want to force your calls to run in series then you need to use the callback on the async call to launch the next one in the series:
function f(times, step) {
step = step || 0;
some_async_call({
callback: function(err, results) {
// Do something with `err` and `results`...
if(step < times)
f(times, step + 1);
}
});
}
f(3);
That approach will execute the three some_async_calls in series but, alas, the initial f(3) will return immediately. One solution to that problem is, of course, another callback:
function f(from_n, upto, and_finally) {
some_async_call({
callback: function(err, results) {
// Do something with `err` and `results`...
if(from_n < upto)
f(from_n + 1, upto, and_finally);
else
and_finally();
}
});
}
f(0, 3, function() { console.log('all done') });
Where does _.times in with all this? No where really. _.times is just a for loop:
_.times = function(n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
_.times exists for completeness and to allow you to add for loop when using _.chain. You could probably shoe-horn it in if you really wanted to but you would be making a big ugly mess instead of simplifying your code.
You could use 250R's async idea but you'd have to build an array of three functions but _.range and _.map would be more appropriate for that than _.times:
// Untested off the top of my head code...
function f(callback) {
some_async_call({
callback: function(err, results) {
// Deal with `err` and `results`...
callback();
}
});
}
var three_fs = _(3).range().map(function() { return f });
async.series(three_fs);
But you still have to modify f to have a callback function and if you're always calling f three times then:
async.series([f, f, f]);
might be better than dynamically building the array with _.range and _.map.
The real lesson here is that once you get into asynchronous function calls, you end up implementing all your logic as callbacks calling callbacks calling callbacks, callbacks all the way down.

This async library might get you started
https://github.com/caolan/async#series
Or if you want to do it yourself, the idea is to do recursive calls after each function callback is called, here's the source code https://github.com/caolan/async/blob/master/lib/async.js#L101

Related

Javascript loop through array asynchronous

I'm making a bot code for a game running on NodeJS and what this function is supposed to do is to loop through an array of vectors and then make the bot go to each vector.
However, what it's actually doing is telling the bot to run to all the vectors at the same time so it spazzes out and then runs to the last vector in the array:
function digSchedule() {
var arrayLength = blocksToMine.length;
for (var i = 0; i < blocksToMine.length; i++) {
console.log(i);
scaffoldTo(blocksToMine[i]);
}
...
}
The function scaffoldTo() needs to be ran and then wait for the bot to do said function then run it for the next element in the array, but I can't figure out how to do it.
There's several ways to achieve this. The first is probably to pass a callback with the "next function to be called" (probably scaffoldTo()). You can use .bind() to create a reference with the iterator index i.
Alternatively, you could set up a loop of Promises, which by definition have a .then() method which executes once the promise is resolved.
Finally, the async/await pattern is similar to Promises, but some find it clearer and it seems to be winning the Hype Wars: https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9.
Callbacks (solution 1) will be available in any version of JS. Promises generally available with a library and have native support in ES6. Async/await is a proposal (?) in ES2017 and is generally well supported.
Here's another way that you could play with this. This way focuses more on the callback style, although it does assume that you can modify the scaffoldTo function, as well as the parameters required for digSchedule
function digSchedule(i, callback) {
if(!i){
i = 0;
}
if(i < blocksToMine.length){
scaffoldTo(blocksToMine[i], digSchedule(i++, callback));
}
else{
callback();
}
}
Then inside of scaffoldTo you would need something like this
function scaffoldTo(block, callback){
//do what ever you need to have the bot go to the vector
//after bot goes to vector call callback
callback();
}
In order to start it all you would just need to call digSchedule with something like this:
digSchedule({null or 0}, function(){
console.log("finished visiting vectors");
});
This does change the pattern from using a for loop like you have, but I think its a fun way to accomplish the goal as well.
That is a good use case to the async functions of ES2017.
Please, try the following:
async function digSchedule() {
var arrayLength = blocksToMine.length;
for (var i = 0; i < blocksToMine.length; i++) {
console.log(i);
await scaffoldTo(blocksToMine[i]);
}
...
}
If ES2017 is out of the question, your best choice would be to make a recursive function that only calls itself again when the promise from scaffoldTo is resolved.
You may use async module to achieve this. Alternatively, you may try something like this
function forEachAsync(array, fun, cb) {
var index = 0;
if (index == array.length) {
cb(null);
return;
}
var next = function () {
fun(array[index], function(err) {
if (err) {
cb(err);
return;
}
index++;
if (index < array.length) {
setImmediate(next);
return;
}
//We are done
cb(null);
});
};
next();
}
forEachAsync([1,2,3,4,5], function(e, cb) {
console.log(e);
cb();
}, function(err) {
console.log('done');
});
Here is how we do with the help of promises.
let numbers = new Array(1,3,2,1);
function asyncFunction(number){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("Number : ",number);
return resolve();
},number*1000);
})
}
let promise = Promise.resolve();
// Here we are using forEach to chain promise one after another.
numbers.forEach(number=>{
promise = promise.then(()=>{
return asyncFunction(number);
});
})
promise.then(()=>{
console.log("Every thing is done!");
})

Javascript function wait fo other one to occur

hello Im new to javascript and I cant figure how to make function wait for other one to finish, here is my code:
// other code
db.transaction(function (transact,callback) {
transact.executeSql('SELECT * FROM buildings WHERE id = ?', [id], function (transact, results) {
if (results.rows.length > 0) {
buildingsCache.push(JSON.parse(results.rows.item(0).data));
index = buildingsCache.length - 1;
}
else {
alert("Building not found in database!");
}
});
});
return buildingsCache[index];
// other code
The problem is that current to function returns value before its set in subfunction. I would need to do something like this:
// other code
var FINISHED=false;
db.transaction(function (transact,callback) {
transact.executeSql('SELECT * FROM buildings WHERE id = ?', [id], function (transact, results) {
if (results.rows.length > 0) {
buildingsCache.push(JSON.parse(results.rows.item(0).data));
index = buildingsCache.length - 1;
}
else {
alert("Building not found in database!");
}
});
FINISHED=true;
});
while(!FINISHED);
return buildingsCache[index];
// other code
but its not working. I checked some other solutions here, but didnt make work any of it. Can you help pls?
You need to change your code so the transaction callback is the one that calls the functions that are waiting for it. Basically, your function to fetch the buildingsCache will not return a value. Instead, it will receive a callback and pass it the buildingsCache when its available. This is the same pattern that the db.transaction function uses
function fetchBuildingsCache(onDone){
db.transaction(function (transact,callback) {
transact.executeSql('SELECT FROM...', [id], function (transact, results) {
//process db results...
//then "return" by calling our callback
onDone(buildingsCache[index]);
});
});
}
//and this is how you use it:
fetchBuildingsCache(function(cache){
console.log(cache);
}
Of course, one side effect of this pattern is that every function that calls fetchBuildingsCache and all functions that call those functions and so on will also have to be converted to callback-passing style.
Sadly, this is unavoidable in Javascript. The best you can do is use a higher level library (like promises or async.js) or use one of those Javascript transpilers that add async/await syntax to Javascript.
Use promises for this purpose.
Take a look at Q js library: http://github.com/kriskowal/q
You've a callback in db.transaction(function (transact,callback), use it properly to execute next set of code once executeSql is done.
A promise represents the eventual result of an asynchronous operation. It is a placeholder into which the result will be materialized.
Use it properly callback vs. promise.

async.eachSeries callback calling multiple times

In this function:
function method2(friends, callback) {
//friends is an array of objects
var ids = _.pluck(friends, 'id'),
arrays = cut(ids, 24),
//cut() splits array into smaller arrays of given length
code = require('fs').readFileSync('...').toString();
var imp,j;
async.eachSeries(arrays, function(i, cb1) {
...
vk.request('execute', {code:code}, function(err, resp, body) {
//vk.request passes its callback to node-request module
//at this point, err is null, and body.error is undefined
if(err || body.error) return cb1(err || body.error);
var arr = body.response;
for(var e in arr) {
if(!arr[e]) return cb1();
async.eachSeries(arr[e], function(i, cb) {
...
cb();
}, cb1);
}
})
}, callback);
}
function is called only once, but async calls callback many times without providing any arguments to it. I cant't see any reasons why. so what's wrong with this code?
I think your problem is here:
for(var e in arr) {
// ...
async.eachSeries(/* ... */, cb1);
You are calling cb1 multiple times, which causes the outermost async.eachSeries to continue multiple times, and therefore the final callback to be called multiple times.
Solution: use async.each instead of a simple for loop to spawn multiple concurrent inner async.eachSeries loops (if that's really what you want). This is the way to nest async loops inline:
async.eachSeries(/* ... */, function(/* ... */, cb1) {
// this body runs once at a time
async.each(/* ... */, function(/* ... */, cb2) {
// this body runs multiple times 'concurrently'
async.eachSeries(/* ... */, function(/* ... */, cb3) {
// this body runs sequentially,
// but multiple sequential runs can happen at once
cb3(/* ... */);
}, cb2);
}, cb1);
}, callback);
An off-topic bonus: Using readFileSync is not advisable except at application startup (if and only if it's safe to use require, it's also safe to use readFileSync). Since you're using async calls, I must assume this is a transactional function, so you should change that to fs.readFile with a callback.
Second bonus: Of course, taken too far, this kind of nesting turns into a big mess. There are ways to combat this using functional programming techniques.

Underscore _.each callback when finished?

Is there a callback for when underscore is finished it's _.each loop because if I console log immediately afterwards obviously the array I am populating with the each loop is not available. This is from a nested _.each loop.
_.each(data.recipe, function(recipeItem) {
var recipeMap = that.get('recipeMap');
recipeMap[recipeItem.id] = { id: recipeItem.id, quantity: recipeItem.quantity };
});
console.log(that.get('recipeMap')); //not ready yet.
The each function in UnderscoreJS is synchronous which wouldn't require a callback when it is finished. One it's done executing the commands immediately following the loop will execute.
If you are performing async operations in your loop, I would recommend using a library that supports async operations within the each function. One possibility is by using AsyncJS.
Here is your loop translated to AsyncJS:
async.each(data.recipe, function(recipeItem, callback) {
var recipeMap = that.get('recipeMap');
recipeMap[recipeItem.id] = { id: recipeItem.id, quantity: recipeItem.quantity };
callback(); // show that no errors happened
}, function(err) {
if(err) {
console.log("There was an error" + err);
} else {
console.log("Loop is done");
}
});
Another option is to build your callback function into the each loop on the last execution:
_.each(collection, function(model) {
if(model.collection.indexOf(model) + 1 == collection.length) {
// Callback goes here
}
});
Edit to add:
I don't know what your input/output data looks like but you might consider using _.map instead, if you're just transforming / rearranging the contents

How to improve the following callback pattern?

I have the following pattern which strings together function1, 2 and 3 through their callbacks.
Assume that function1, 2 and 3 can take up to 1 second to complete. I would like to know other "better" ways of doing the same so that it doesn't turn into a monster when more callback functions are nested.
function1(function(cbData1){
if(cbData1){
function2(cbData1, function(cbData2){
if(cbData2){
function3(cbData2, function(cbData3){
// success
}
} else {
// failed for reason#2
}
});
} else {
//failed for reason#1
}
});
//example function
function function2(data, callback) {
// do dirty things
callback(newData);
}
If I understand you correctly you need to organize the callbacks in a chain. Look at Chain of Responsibility pattern.
So you will create an object containing the function to execute and callback function to execute if needed.
The last time I played with really nasty callbacks, I ended up doing something like this:
// Typed on the fly, be kind
var callbackList = []; // A list of functions to call in order.
function doNextCallback() {
if (callbackList.length) {
var f = callbackList.shift(); // Get the next callback function
window.setTimeout(f); // Give breathing space.
}
}
// Set up our callbacks
callbackList.push(f1);
callbackList.push(f2);
callbackList.push(f3);
// Start it happening.
doNextCallback();
function f1() {
console.log("Busy busy");
doNextCallback();
}
function f2() {
console.log("Busy busy");
doNextCallback();
}
function f3() {
console.log("Busy busy");
doNextCallback();
}
I had it all wrapped up in a nice object, but you get the idea.
This also made it very easy to re-arrange callbacks or to call just two of them in a big loop for testing purposes.

Categories

Resources