nodejs async same arguments to an array of functions? - javascript

How do I apply the same arguments to an array of functions?
Works:
async.parallel([func(arg, arg2), func2(arg, arg2)],
function() {
next();
});
The array has indeterminate / various functions functions inside though, so I'm not sure which functions I should be sending to the parallel method.
In various files I'm building the functions array:
funcArray.push(func2)
As a result I have an array like this:
[func, func2]
I would like to just do:
async.parallel(funcArray,
function() {
next();
});
and have all the same arguments be applied to all the functions. How can I do this? Thanks.
Ended up writing my own:
_.each(obj, function (func) {
func(req, res, function () {
i++
if (i == objSize) {
next()
}
})
})

Untested, but this ought to work:
var args = [ arg, arg2 ]
, funcCalls = []
;
for(var i = 0; i < funcArray.length; i++) {
funcCalls.push(function() {
funcArray[i].apply(this, args); }
);
}
async.parallel(funcCalls, next);
Or, if you already know how many arguments you'll have, you don't need to use apply in the middle section:
for(var i = 0; i < funcArray.length; i++) {
funcCalls.push(function() {
funcArray[i](arg, arg2); }
);
}
And finally you could really tighten it up with a map function as provided by e.g. Underscore.js:
async.parallel(_.map(funcArray,
function(func) { return function() { func(arg, arg2); } }
), next);
...but then the next guy who comes across your code might kill you.

Related

What meaning is return a(function () )?

I studied simple asynchronized JavaScript code, but stuck with return value(function() ). I don't understand it.
How does operate this return value(function())?
function _async(func) {
return function () {
arguments[arguments.length++] = function (result) {
_callback(result);
};
(function wait(args){
for (var i = 0; i < args.length; i++) {
if (args[i] && args[i].name == '_async_cb_receiver')
return args[i](function (arg) {
args[i] = arg; wait(args);
})}
func.apply(null, args);
})(arguments);
var _callback;
function _async_cb_receiver(callback) {
_callback = callback;
}
return _async_cb_receiver;
};
}
return args[i](function (arg) {
args[i] = arg; wait(args);
})
I don't understand this part.
wait(args) just returns args[i], but parentheses appear and anonymous function is executed. How does this function handle args[i] and what arguments are in arg?
args is an array of functions. So args[i] is one of these functions, and args[i](something) calls that function with something as the argument.
In this case, something is another function. This function replaces args[i] with its own argument, then calls wait(args) recursively.
This code is pretty convoluted, so I'm not sure what it's doing as a whole. I suspect it's doing something similar to the way promises work, but was written before they were added to the language.

In JavaScript, how to execute next function from an array of functions

I have an array of functions, as in:
funcArray = [func1, func2, func3];
When in a given function, I want to execute the next function in the array. How do I do this? Here is my basic skeleton:
function func1() {
// I get current function caller
var currentFunc = func1.caller;
// I want to execute the next function. Happens to be func2 in the example.
}
I cannot use indexOf function, as one would for an array of strings or numbers.
NOTE: This question appears to be similar to this and the one it refers to. However, it is a different question.
I want to alter the sequence of processing by merely modifying the array. That's the goal. A possibly more efficient approach would be appreciated.
Clarification: Based upon some of the comments:
funcArray is global.
The goal is to implement middleware for a Node.js HTTP module in as simple and efficient a manner as possible without using any third-party modules.
Unless func1 closes over funcArray, you cannot have it reach out and find func2 and execute it, nor should you. Even if func1 does close over funcArray, it would be poor separation of concerns for func1 to reach out and find itself in funcArray and then execute func2.
Instead, have other code that's in charge of running the functions.
If they're synchronous
If the functions complete their work synchronously, then it's simply:
funcArray.forEach(fn => fn());
or
for (const fn of funcArray) {
fn();
}
or if the result of one function should be passed to the next, you can use reduce:
const finalResult = funcArray.reduce((previousResult, fn) => fn(previousResult), undefined);
...where undefined is the value to pass to func1.
If they're asynchronous
If they don't do their work synchronously, you'll need to provide them a way to notify their caller that they've completed their work. Promises are a good, standard way to do that, but you could use simple callbacks instead.
If you make them return promises, for instance, you can use the old promise reduce trick:
funcArray.reduce((p, fn) => {
return p.then(() => {
fn();
});
}, Promise.resolve());
or if the result of one function should be passed to the next:
funcArray.reduce((p, fn) => {
return p.then(fn);
}, Promise.resolve());
You can provide an argument to Promise.resolve to set the value to pass to func1 (without one, it'll receive undefined).
You can bind to the function the index where it is in the array so you can use this index to get and call the next function:
var funcArray = [func1, func2];
var boundFuncArray = funcArray.map((f, i) => f.bind(null, i));
boundFuncArray[0]();
function func1(nextFunctionIndex) {
console.log('func1 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
function func2(nextFunctionIndex) {
console.log('func2 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
As T.J Crowder stated in the comment below, you can also bind the next function to the current one:
var funcArray = [func1, func2];
var boundFuncArray= funcArray.map((f, i, arr) => f.bind(null, arr[i + 1]));
boundFuncArray[0]();
function func1(nextFunc) {
console.log('func1 called');
// Execute next function:
nextFunc && nextFunc();
}
function func2(nextFunc ) {
console.log('func2 called');
// Execute next function:
nextFunc && nextFunc();
}
You can get the current function's name with arguments.callee.name, loop through the array of functions, and call the next function:
funcArray = [func1, func2, func3];
// Only func1() and func2() will be documented since the others have repeating code
function func1() {
// show the current function name
console.log(arguments.callee.name);
// loop the array of functions
for(var i = 0; i < funcArray.length; ++i)
{
// when the current array item is our current function name and
// another function exists after this then call it and break
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func2() {
console.log(arguments.callee.name);
// some logic which switches our next function to be func4()
funcArray[2] = func4;
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func3() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func4() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
// call the first function
funcArray[0]();
Output:
func1
func2
func4
I have solved it this way:
// Adding next options to array
function addNext(array) {
array.last = 1
Object.defineProperty(array, 'next', {get:
function() {
if(this.last < this.length) {
this.last++
return this[this.last-1]
} else {
this.last = 1
return () => {}
}
}
});
}
// The functions for array (has to be function and not arrow function)
function first(param) {
console.log('first',param)
return this.next(param)
}
function second(param) {
console.log('second',param)
return this.next(param)
}
function third(param) {
console.log('third',param)
return this.next(param)
}
// The array
let fns = [first,second,third]
// Adding next option to array
addNext(fns)
// Run first function from array
fns[0]('test')
I dont know if your functions require certain parameters but this is the first thing that came to my mind.
var functArray = [
function() {
console.log("function1 executed");
},
function() {
console.log("function2 executed");
},
function() {
console.log("function3 executed");
},
function() {
console.log("function4 executed");
}];
functArray.forEach(function(x){
x();
});
The accepted answer and other comments did help me, but the way I implemented it is as follows:
//The functions are defined as variables.
//They do not get hoisted, so must be defined first.
func1 = function (arg1, arg2) {
//Code to do whatever...
...
//Execute the next function.
//The name of the function is returned by executing nextFunc()
global[nextFunc()](arg1, arg2, arg3);
}
func2 = function (arg1) { //Note different type of args
...
}
//Note that this is an array of strings representing function names.
funcArray = ["func1", "func2", "func3",...]
//Start the execution...
func1(arg1, arg2);
function nextFunc() {
var currentFuncName = nextFunc.caller.name;
var index = funcArray.indexOf(currentFuncName);
if (index < funcArray.length)
return funcArray[index+1];
}
The sequence of functions to be executed is easily managed through the array funcArray. The number or type of arguments is not fixed for each function. Additionally, the functions control if they should stop the chain or continue with the next function.
It is very simple to understand requiring basic Javascript skills. No overheads of using Promises.
"global" gets replaced by "window" for browser. This is a Node.js implementation. The use of function names in the array will, however, break if you minify the JS code. As I am going to use it on the server, I do not expect to minify it.
You can do it in this way with promise.all if your functions to be executed in parallel.
let toBeExecutedList = [];
toBeExecutedList.push(() => this.addTwoNumber(2, 3));
toBeExecutedList.push(()=>this.square(2));
And Then wherever you want to use them, do it like this:
const resultArr = await Promise.all([
toBeExecutedList.map(func => func()),
]);

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!");
})

node.js - nesting request, wait until request function is done

I use the following code
request(firstparams, function () {
var secondparams = {
// ******
};
request(secondparams, function () {
for (i=0; i<3; i++) {
var thirdparams = {
// ******
};
request(thirdparams, function () {
console.log('foo');
});
}
console.log('bar');
});
});
and want to get the result like:
foo
foo
foo
bar
but the result is:
bar
foo
foo
foo
Sorry for my poor English, if there is something ambiguity,I would try my best to explain. Thanks a lot ^ ^
The easies way to do what you want would be to use 'async' module which is the classical module to handle async problems.
To run 3rd calls in parallel and log 'bar' after each of them is finished you would do something like this:
const async = require('async');
let asyncFunctions = [];
for (let i = 0; i < 3; i+= 1) {
let thirdParams = {...};
asyncFunctions.push(function (callback) {
request(thirdparams, function (err, data) {
console.log('foo');
callback(err, data);
});
});
}
async.parallel(asyncFunctions, function (err, data) {
console.log('bar');
});
You are using callbacks. But there are also other ways to handle asynchrony in node.js like: promises, generators and async/await functions from ES7.
I think that may be useful for you do check out some articles like this one.

Weird index issue in Backbone click event callback

I have this code, it's independent and isolated. The problem I am having is that the index i is starting at 1 instead of starting at 0. I have no idea why this could be, and doesn't seem to have anything to do with the closure that I am pushing into the deletes array...but I can't be sure, no idea what the issue is.
onClickResetAll: function (event) {
event.preventDefault();
var deletes = [];
Object.keys(collections).forEach(function (key) {
if (collections.hasOwnProperty(key)) {
var coll = collections[key];
for (var i = 0; i < coll.models.length; i++) {
deletes.push(function (callback) {
var index = i; //i starts at 1, not 0 !!!
coll.models[index].deleteModel(function (err, resp, x) {
console.log(err, resp, x);
if(err){
callback(err);
}
else{
callback(null,null);
}
});
});
}
}
});
async.parallel(deletes,function(err,results){
Backbone.Events.trigger('bootRouter', '+refreshCurrentPage');
});
}, //end of onClickResetAll callback function
//end
The problem isn't really that i starts at one, the problem is that i will be coll.models.length for every function in deletes. Why would that be? Well, each function is sharing the same i and i won't be evaluated until the functions inside deletes are actually called.
The solution is to force i to be evaluated when it has the value you want (i.e. evaluated i when you're building the callback function). There are various solutions and they're all variations on the "wrap it in a function to break the reference" theme:
Use an iterator with a callback function instead of a plain for loop:
coll.each(function(model, i) {
// `model` is the model from the collection, `i` is the loop index.
});
You can use each here because Backbone collections have a bunch of Underscore functions built in.
Wrap the loop body in an SIF:
for(var i = 0; i < coll.models.length; ++i)
(function(i) {
//...
})(i);
Use a separate function to build your functions:
function make_deleter(coll, i) {
return function(callback) {
coll.models[i].deletedModel(function(err, resp, x) {
//...
}
}
}
//...
for(var i = 0; i < coll.models.length; ++i)
deletes.push(make_deleter(coll, i));
They all do pretty much the same thing: add an extra function call into the mix to force i to be evaluated (rather than just referenced) on each iteration of the loop.
In a Backbone situation, 1 would probably be the most natural and you wouldn't even need your troublesome i with that approach.
another solution to this is using async.each or async.eachSeries instead of async.parallel. using the first 2 avoids pushing to an array of functions altogether.

Categories

Resources