function(obj){
for (property in obj) {
if (obj.hasOwnProperty(property)) {
// some code here
if(condition){
obj.children = example.getdata(base, obj.name);
}
// some more code releated to obj
}
}
if (obj.length == 10)
{
//some more function
}
}
Now i want to make example.getdata async but i dont want to execute if statement that is after for loop until all the sync tasks are done.
Its more of like i want all the example.getdata function calls to execute in parallel and after they finish work i execute if (obj.length).
I tried using promises and push all the promises and resolve them but i dont know how to handle the return value for each function call.
You can use Promise s.
Think of something like:
var jobs = get_jobs(data);
when_all(jobs).done(function (jobs_result) {
console.log(jobs_result);
});
Where get_jobs returns a list or Promises and when_all will resolve when all it's input jobs are resolved.
To run async tasks serially in a loop, you can't use a regular for loop because the for loop won't wait for your async operation to complete before executing the next cycle of the loop. Rather, you have to do your own custom iteration a slightly different way. Here's one way:
Assuming your getdata() function is actually asynchronous and has a completion function, you could structure things like this:
function myFunc(obj, callback) {
var keys = Object.keys(obj);
var cntr = 0;
var results = [];
function next() {
if (cntr < keys.length) {
example.getdata(obj[keys[cntr++]], function(result) {
// do something with the result of each async operation here
// and put it in the results array
// kick off the next iteration
next();
});
} else {
// call the final callback because we are done now with all async operations
callback(results);
}
}
// start the first iteration
next();
}
If your getData function returns a promise or can be made to return a promise, then you can use promises for this too:
function myFunc(obj) {
var keys = Object.keys(obj);
keys.reduce(function(p, item) {
return p.then(function(result) {
// do something with the result of each async operation here
// and put it in the results array
return example.getdata(obj[item]);
});
}, Promise.resolve());
}
myFunc.then(function(results) {
// all async operations done here, use the results argument
});
Here is a clean example of a for loop using promises. If you can pick a promise library libraries like Bluebird will make it even simpler:
function(obj){
var props = Object.keys(obj), p = Promise.resolve();
props.forEach(function(prop){
p = p.then(function(){
if(condition){ // can access obj[prop] and obj here
obj.children = example.getData(...); // getData returns a promise
return obj.children; // return the promise.
}
});
});
Promise.resolve(obj.children).then(function(children){
// here the async call to get `.children` is done.
// can wait for `p` too (the loop) if we care about it
if(obj.length === 10) // do stuff
});
Related
I want to block any other execution until evaluateTestCases function finishes but before the function finishes, It goes in other function that is then of same function.
Here is the function:
function evaluateTestCases(source, lang)
{
return new Promise(function(resolve)
{
for(let i=0;i<hiddenData.testCases.length;i++)
{
getOutput(source, lang, hiddenData.testCases[i].input).then(function(response){
console.log(hiddenData.testCases[i].output.toString().trim()===response.output.toString().trim());
if(hiddenData.testCases[i].output.toString().trim()===response.output.toString().trim())
{
points+=1;
}
});
}
resolve(points);e
});
}
Here is the function call:
$("#submit").on("click",function()
{
evaluateTestCases(editor.getValue(), $('#language').val()).then(function(result){
console.log(result);
});
});
Now, what happens is: before evaluateTestCases function finishes off, console.log(result); gets executed and since result is not yet defined, it acts as an error for me.
You are creating promises in the loop, but you are not waiting for them to be settled.
Either use Promise.all (if multiple getOutput calls may be triggered before they signal completion), or use await getOutput(...). The latter solution will wait for each returned promise to be settled before continuing into the next iteration of the loop. You need to make the outer function async:
async function evaluateTestCases(source, lang) {
let points = 0; // Not sure where you were initialising this...
for (let i = 0; i < hiddenData.testCases.length; i++) {
const response = await getOutput(source, lang, hiddenData.testCases[i].input);
console.log(hiddenData.testCases[i].output.toString().trim() === response.output.toString().trim());
if (hiddenData.testCases[i].output.toString().trim() === response.output.toString().trim()) {
points+=1;
}
});
return points;
}
The main function call can remain like you had it.
Supposing we have a script that will execute a certain task for each row in an array.
function execute(err, array){
loop(array, function(err,object){
console.log(object)
//do a certain task when it's finished get into the next object successively.
});
}
function loop(array,callback){
array.forEach(function(object){
callback(null, object);
});
}
function array(callback){
callback(null, [1, 2, 3, 4, 5]);
}
setTimeout(function(){
array(execute);
}, 6000);
Questions:
How to get into the next loop only after finishing the task?
Is my function considered asynchronous ?
You can do something like this to iterate over your array :
var myarray = [1,2,3,4,5];
next(myarray, 0);
function next(array, idx) {
if (idx !== array.length) {
// do something
console.log(array[idx]);
// run other functions with callback
another_fct(array[idx], function() {
next(array, idx + 1); // then the next loop
});
// or run directly the next loop
next(array, idx + 1);
} else {
// the entire array has been proceed
console.log('an array of '+array.length+' number of elements have been proceed');
}
}
function another_fct(array_element, callback) {
// do something with array_element
console.log('value : '+array_element);
callback(); // run the next loop after processing
}
This method will perform your array elements synchronously.
Try this:
function execute(err, array) {
loop(array, function(err, object, next) {
console.log(object);
next(); // this will call recur inside loop function
}, function() {
console.log('All done');
});
}
function loop(array, callback, finish) {
var copy = array.slice();
(function recur() {
var item = copy.shift();
if (item) {
callback(null, item, recur);
} else {
if (typeof finish == 'function') {
finish();
}
}
})();
}
No, you function is not asynchronous but you call it asynchronously using setTimeout.
async provides async.series and other helpers for this purpose.
You can wrap your head around it better if you refactor the code and remove any redundant anonymous functions. The case is, that passing an anonymous function as an argument to another function not yet make the function asynchronous. You read more a more in depth explanation in "Does taking a callback make a function asynchronous?".
From the refactored version,
setTimeout(function() {
[1, 2, 3, 4, 5].forEach(function(object) {
console.log(object)
//do a certain task when it's...
});
}, 6000);
it can be seen that forEach is called for the array. The forEach method executes provided function once per array element and does it synchronously.
So, to answer your question:
yes, it calls the next item only after finishing previous (but if doing anything asynchronous, see below)
this is not considered asynchronous since it doesn't perform any asynchronous operations (not considering the outer setTimeout)
However, if you choose to start an asynchronous operation in the forEach function, then things change considerably. The result is that all operations are in-progress at the same time. This is potentially a hazardous operation resource-wise. There are libraries such as async for handling this use case gracefully.
(Btw, good use of Node.js errbacks, where first function argument reserved for passing potential error.)
I'm trying to work through this js/async scenario and i'm trying to know how the rest of the js world handles this.
function doStuff(callback) {
cursor.each(function(err, blahblah) {
...doing stuff here takes some time
});
... Execute this code ONLY after the `cursor.each` loop is finished
callback();
EDIT
Here's a more concrete example updated using most of the suggestions below which still doesn't work.
function doStuff(callback) {
MongoClient.connect(constants.mongoUrl, function(err, db) {
var collection = db.collection('cases2');
var cursor = collection.find();
var promises = []; // array for storing promises
cursor.each(function(err, item) {
console.log('inside each'); // NEVER GETS LOGGED UNLESS I COMMENT OUT THIS LINE: return Q.all(promises).then(callback(null, items));
var def = Q.defer(); // Create deferred object and store
promises.push(def.promise); // Its promise in the array
if(item == null) {
return def.resolve();
}
def.resolve(); // resolve the promise
});
console.log('items'); // ALWAYS GETS CALLED
console.log(items);
// IF I COMMENT THIS LINE OUT COMPLETELY,
// THE LOG STATEMENT INSIDE CURSOR.EACH ACTUALLY GETS LOGGED
return Q.all(promises).then(callback(null, items));
});
}
without using promises or any other dependencies/libraries you can simply
function doStuff(callback) {
add a counter
var cursor = new Array(); // init with some array data
var cursorTasks = cursor.length;
function cursorTaskComplete()
{
cursorTasks--;
if ( cursorTasks <= 0 ) {
// this gets get called after each task reported to be complete
callback();
}
}
for ( var i = 0; i < cursor.length; i++ ) {
...doing stuff here takes some time and does some async stuff
check after each async request
...when async operation is complete call
cursorTaskComplete()
}
}
Without knowing the details of the async calls you're making within the cursor.each loop, I shall assume that you have the ability to invoke a callback each time the functions invoked therein have completed their async task:
function doStuff() {
var promises = []; // array for storing promises
cursor.each(function(err, blahblah) {
var def = Q.defer(); // create deferred object and store
promises.push(def.promise); // its promise in the array
call_async_function(..., def.resolve); // resolve the promise in the async function's callback
});
// pass the array to Q.all, only when all are resolved will "callback" be called
return Q.all(promises);
}
and the usage then becomes:
doStuff().then(callback)
Note how the invocation of the callback now never touches the doStuff function - that function now also returns a promise. You can now register multiple callbacks, failure callbacks, etc, all without modifying doStuff. This is called "separation of concerns".
[NB: all the above based on the Q promises library - https://github.com/kriskowal/q]
EDIT further discussion and experimentation has determined that the .each call is itself async, and gives no indication to the outside when the last row has been seen. I've created a Gist that demonstrates a resolution to this problem.
if you want to do it with the async module, you can make use of the async forEachSeries function
Code snippet:
function doStuff(callback) {
async.forEachSeries(cursor, function(cursorSingleObj,callbackFromForEach){
//...do stuff which takes time
//this callback is to tell when everything gets over execute the next function
callbackFromForEach();
},function(){
//over here the execution of forEach gets over and then the main callback is called
callback();
});
}
In my mind an elegant/ideal solution would be to have something like
cursor.each(........).then( function() { ....your stuff});
But without that you can do this....UPDATED
http://plnkr.co/edit/27l7t5VLszBIW9eFW4Ip?p=preview
The gist of this is as shown below...notice....when
var doStuff = function(callback) {
cursor.forEach(function(cursorStep) {
var deferred = $q.defer();
var promise = deferred.promise;
allMyAsyncPromises.push(promise);
cursorStep.execFn(cursorStep.stepMeta);
promise.resolve;
});
$q.when(allMyAsyncPromises).then(callback);
}
After hitting the start button wait for few seconds...the async tasks have been simulated to finish in 5 seconds so the status will update accordingly.
Not having access to a real cursor object..I had to resort of fake cursor like and array.
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 am using Jaydata as API for HTML5 indexedDB. I have a table in indexedDB where I need to query recursively. I need a callback when entire process is completed. Following is the recursive function. I need to have a callback when everything is done.
function getData(idValue) {
myDB.MySplDB
.filter( function(val) {
return val.ParentId == this.parentId;
}, {parentId: idvalue})
.toArray( function(vals) {
if(vals.length < 1) {
// some operation to store the value
} else {
for (var j=0;j<vals.length;j++) {
getData(vals[j].Id);
}
}
});
}
Adding .done(function(){...}); to .toArray doesn't work since it gets called before completion.
(Disclaimer: I work for JayData)
To wait for the finish of the entire process you need to use promises. You always have to return a promise. In the loop it gets tricky, return a super promise. So the code should be something like this:
function getData(idValue) {
return myDB.MySplDB
.filter( function(val) {
return val.ParentId == this.parentId;
}, {parentId: idvalue})
.toArray( function(vals) {
if(vals.length < 1) {
// some operation to store the value
// important: return a promise from here, like:
return myDB.saveChanges();
} else {
var promises = [];
for (var j=0;j<vals.length;j++) {
promises.push(getData(vals[j].Id));
}
return $.when.apply(this, promises);
}
});
}
getData(1)
.then(function() {
// this will run after everything is finished
});
remarks:
this example uses jQuery promises, so you'll need jQuery 1.8+
$.when uses varargs hence we need the apply
this can work with q promise with a slightly different syntax
Would this pseudo-code make sense in your case ?
var helper = function (accu) {
// Take an id from the accumulator
// query the db for new ids, and in the db query callback :
// If there is child, do "some operation to store the value" (I'm not sure what you're trying to do here
// Else add the child to the accumulator
// if accu is empty, call the callback, it means you reached the end
getData() would call this helper with an accumulator containing the first id, and your final callback