I am new to NodeJS and I am not really sure how should the following function be declared. The function contains only a for loop which generates a string. There are no "heavy-weight" calculations done.
Variant 1:
function getRandomString(arg) {
for (var i = 0; i < 100; i++) {
// ...
}
return string;
}
var randomString = getRandomString(arg);
// ... an async code which will use the string
Variant 2: Or should I make it async (async-style)? It would look something like this:
function getRandomString(arg, callback) {
for (var i = 0; i < 100; i++) {
// ...
}
callback(string);
}
getRandomString(arg, function(randomString) {
// Async code...
});
Or should I make it async? So something like this:
function getString(arg, callback) {
for(var i = 0;i<100;i++) {
// ...
}
callback(string);
}
No. That code does still run synchronousls, only with an odd callback style for returning the result. Since JS does no tail call optimisation or has continuation support, it just introduces the pyramid of doom without any benefit.
Do not use this unless you really make it asynchronous (setTimeout, nextTick etc), for example to defer the single loop iterations.
There is no benefit from making it "asynchronous". In fact, the code will run synchronously, with the getString method only exiting after the callback is complete.
The only decision you really have to make is one of coding style (Do you want it to seem asynchronous or not.)
function getString(arg, callback)
{
for(var i = 0;i<100;i++)
{
// ...
}
//Callback will execute synchronously, and you will wait here until it is complete
callback(string);
}
Related
I have a few functions that need to be performed in order and JavaScript tends to do them asynchronous. I've looked into various methods of solving this problem including using callbacks, creating my own promise, a Promise.all() technique, and finally a newer version using async functions and await. I still wasn't able to get the code to run the way I wanted to.
The idea is, run initialize() first, initialize calls colorcells, and finally last thing to run is draw_path.
function initialize() {
for (let i = 1; i < 20; i++) {
setTimeout(() => {
colorcells(i)
}, i * 30)}
}
function colorcells (cell){
// then execute this function from initialize
}
function draw_path(){
// this should be the last function to get executed
}
async function init(){
await initialize()
draw_path()
}
// starts our code
init()
You could use promise, but it would make more sense to code a "queue" and when it is done, you call the next step.
const max = 20;
let current = 1;
function colorNext() {
colorCell(current);
current++;
if (current < max) {
window.setTimeout(colorNext, 30);
} else {
drawPath();
}
}
function colorCell(cell) {
console.log("Color", cell);
}
function drawPath() {
console.log('draw called');
}
colorNext();
It can be done with promises, but there is no need to read all those timeouts.
Lets say I've got the following code,
function someFunction(){
var i = 0;
while (i < 10){
someAsyncProcess(someField, function(err, data){
i++;
// i want the next iteration of the while loop to occur after this
}
}
}
The someAsyncProcess runs, and increments 'i'.
However, due to the async nature of JavaScript, the while loop runs thousands of times before i = 10. What if I want the while loop to run exactly 10 times. What if I want the while loop to execute the code inside only after the callback function has finished executing.
Is it possible to do this without a setTimeout function?
Please note that I am still relatively new to JavaScript so if I incorrectly used any jargon, correct me.
while is synchronous. You cannot make it wait until an asynchronous process is done. You cannot use a while loop in this case.
Instead you can put your code in a function and call the function itself again if the condition is met.
Example:
function someFunction() {
var i = 0;
function step() {
if (i < 10) {
someAsyncProcess(someField, function(err, data) {
i++;
step();
});
}
}
step();
}
There are quite a few libraries out there that provide ready-made solutions for this. You might also want to look into promises in general.
Use node package q which will help you return promises. You can achieve the same in the following manner using q.
var q = require('q');
function someFunction(){
var promises = []
var i = 0;
while (i < 10) {
promises.push(function() {
i++;
//write code here for using current value of i
})
}
return q.all(promises);
}
You can call someFunction() as below
someFunction().then(function(arrayOfResponseFromEachPromiseFunction) {
console.log(JSON.stringify(arrayOfResponseFromEachPromiseFunction, null, 4));
}).catch(function(err){
console.log(err);
}).done();
Please rectify if you find any syntax error. Hope it helps.
You need to use replace while loop Iteration with function recursion
function someFunction() {
var i = 0;
(function asyncWhile() {
if (i < 10) {
someAsyncProcess(someField, function(err, data) {
//You code here
i++;
asyncWhile();
});
}
})(); //auto invoke
}
I have a for loop that kicks off hundreds of async functions. Once all functions are done I need to run one last function but I can't seem to wrap my head around it knowing when all functions are complete.
I've tried promises but as soon as any of the functions in the loop resolve then my promise function completes.
for(var i = 0; i < someArray.length; i ++){
// these can take up to two seconds and have hundreds in the array
asyncFunction(someArray[i];
}
How can I tell once every function has completed?
An increment
You can add a callback which increments:
for (var i = 0; i < len; i++) {
asycFunction(someArray[i]);
asycFunction.done = function () {
if (i == someArray.length - 1) {
// Done with all stuff
}
};
}
A recursive approach
This type of approach is more liked by some developers but (might) take longer to execute because it waits for one to finish, to run another.
var limit = someArray.length, i = 0;
function do(i) {
asyncFunction(someArray[i]);
asyncFunction.done = function () [
if (i++ == someArray[i]) {
// All done!
} else { do(i); }
}
}
do(i++);
Promises
Promises aren't well supported at the moment but you can use a library. It will add a little bulk to your page for sure though.
A nice solution
(function (f,i) {
do(i++,f)
}(function (f,i) {
asyncFunction(someArray[i]);
asyncFunction.done = function () {
if (i++ === someArray.length - 1) {
// Done
} else { f(i) }
};
}, 0)
Many libraries have .all resolver:
jQuery
q
bluebird
and many more - https://promisesaplus.com/implementations
You can use them or learn their source code.
Assuming the code to be the body of function foo() :
function foo() {
return Promise.all(someArray.map(function(item) {
//other stuff here
return asyncFunction(item, /* other params here */);
}));
}
Or, if there's no other stuff to do, and no other params to pass :
function foo() {
return Promise.all(someArray.map(asyncFunction));
}
You can check number of response.
For every response you can increase counter value and if counter value same as someArray.length then you can assume all Async functions are done and can start next step.
I have a list of items, which I want to run an async task on each.
I want to synchronize so each element will be processed once the preceding element has completed. What I've tried so far is:
function processItems(items) {
var i = 0;
while(i < items.length) {
asyncFunc(items[i], function() { i++ }); // asyncFunc takes a callback parameter
}
}
However this loops forever (I believe i is out of scope in the callback function).
Is there a better way to achieve this?
I think the following accomplishes what you're looking for:
function processItems(items) {
var i = 0,
length = items.length,
fn = function() {
if(i < length) {
asyncFunc(items[i], fn);
i++;
}
};
fn();
}
fn is a function that sets the callback equal to itself as long as the index is less than the length. Then I call fn once to start it off. Here's a fiddle:
http://jsfiddle.net/8A8CG/
Alternatively, if asyncFunc returns a promise, you can use Array#reduce to process the items in a series:
function processItems(items) {
return items.reduce((promise, item) => {
return promise.then(() => asyncFunc(item));
}, Promise.resolve());
}
If you need to execute a set of async function in series, I highly recommend the async module for node.
It offers a series method to execute async tasks in series. It also offers a waterfall method that will pass the results of the previous task to the next task.
Oops. You are looking for eachSeries. Here's how it might work for you:
var async = require('async');
async.eachSeries(items, asyncFunc, function(err){ if(err) return "OH NO"; return "Yay!"})
Make sure asyncFunc invokes a callback with either an error or null.
I have a JavaScript function like the following.
function changeTheDom(var1, var2, var3) {
// Use DWR to get some server information
// In the DWR callback, add a element to DOM
}
This function is called in a couple of places in the page. Sometimes, in a loop. It's important that the elements be added to the DOM in the order that the changeTheDom function is called.
I originally tried adding DWREngine.setAsync(false); to the beginning of my function and DWREngine.setAsync(true); to the end of my function. While this worked, it was causing utter craziness on the rest of the page.
So I am wondering if there is a way to lock the changeTheDom function. I found this post but I couldn't really follow the else loop or how the lockingFunction was intended to be called.
Any help understanding that post or just making a locking procedure would be appreciated.
Don't try to lock anything. The cleanest way is always to adapt to the asynchronous nature of your code. So if you have an asynchronous function, use a callback. In your particular case I would suggest that you split your function up in one part that is executed before the asych call and one part that is executed afterwards:
function changeTheDomBefore(var1, var2, var3) {
//some code
//...
asyncFunction(function(result){
//this will be executed when the asynchronous function is done
changeTheDomAfter(var1, var2, var2, result);
});
}
function changeTheDomAfter(var1, var2, var3, asynchResult) {
//more code
//...
}
asyncFunction is the asynchronous function which, in this example, takes one argument - the callback function, which then calls your second changeTheDom function.
I think I finally got what you mean and I decided to create another answer, which is hopefully more helpful.
To preserve order when dealing with multiple asynchronous function calls, you could write a simple Queue class:
function Queue(){
var queue = [];
this.add = function(func, data) {
queue.push({func:func,data:data});
if (queue.length === 1) {
go();
}
};
function go() {
if (queue.length > 0) {
var func = queue[0].func,
data = queue[0].data;
//example of an async call with callback
async(function() {
func.apply(this, arguments);
queue.shift();
go();
});
}
}
};
var queue = new Queue();
function doit(data){
queue.add(function(result){
console.log(result);
}, data);
}
for (var i = 0; i < 10; i++) {
doit({
json: JSON.stringify({
index: i
}),
delay: 1 - i / 10.0
});
}
FIDDLE
So everytime you invoke your async function, you call queue.add() which adds your function in the queue and ensures that it will only execute when everything else in the queue is finished.