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
Related
I've been reading about async functions in JavaScript and found out that I don't quite understand the following piece of code that comes from here.
Here it is:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
What I don't understand is where all these results come from.
They are callbacks. A callback is just simply a function which is passed as an argument to another function and is used to do something with the result of some operation done by the function which receives the callback as an argument. You can write your own. A simple example:
function callMe(callback) {
let addition = 2 + 2;
callback(addition);
}
callMe(function(result) {
console.log(result);
});
callMe calls its callback function with the result of 2 + 2. You then receive that result inside the callback function when you use callMe, and you can then write your own custom code to do whatever you want with it.
The beauty of JavaScript is that you already have all you need to test it.
The code you posted references 4 functions (don't forget the failure callback):
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
Those functions are not written. So let's write them:
var failureCallback = function() {
console.log('something went wrong');
}
var doSomething = function(callback) {
window.setTimeout(function() {
var result = Date.now(); // Create some data that will change every time the function is invoked
callback(result);
}, 500);
}
var doSomethingElse = function(res, callback) {
window.setTimeout(function() {
var result = [res]; // all this function really does is put the first argument in an array
callback(result);
}, 500);
}
function doThirdThing(res, callback) {
window.setTimeout(function() {
res.push(Math.PI); // res is supposed to be an array, so we can add stuff to it.
var result = res;
callback(result); // here we could have used res as a value, but I kept it consistent
}, 500);
}
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ', finalResult); // switched to a comma to display properly in the console
}, failureCallback);
}, failureCallback);
}, failureCallback);
To create some asynchronicity I used setTimeout so overall you have to wait 1.5 seconds.
The 3 functions that use a callback function as an argument, simply execute the function they were given. This is absolutely standard in JavaScript where functions are first class objects: you can pass them around as arguments like any other value type.
Javascript is a single threaded language, callbacks are used as a way to control the flow of execution when a asynchronous block of code ends in a non-blocking way. A callback is normally just another function (function B) that is passed into the asynchronous function (function A) to run when function A completes. I.E
doSomething(func) {
// do async code.
// call func
// func()
}
you haven't posted the inner blocks of those functions but i think we can all safely assume "result" is the response from the server passed back into the callback function I.E
doSomething(callback) {
//fetch or ajax or whatever to server store response.
//pass response BACK to callback function
callback(response)
}
I'm trying to do something fairly simple it seems in NodeJS - I want to run functions, one at a time. All of these functions have callbacks. I have outlined my code below, as well as the function that they run for further reference.
My problem is that the first two are working absolutely fine - one at a time, but the third iteration simply ignores the first two functions and just goes anyway. This is causing a real problem, since my program works with putting objects into a database, and it's causing duplicate objects.
The overall goal is to simply have each function run one at a time. Is there anything I'm missing here? Thanks so much for your help!
Please note that in the functions below, I have simplified all parameters to "args" for easier reading.
Calling the functions:
addNewProject(args);
addNewProject(args);
addNewProject(args);
Inside the functions, I run this:
function addNewProject(args) {
var info = args;
queue.push(function (done) {
loopThroughDetails(info, projID, 0, function () {
console.log('complete');
done(null, true);
});
});
}
This calls loopThroughDetails(), which is an integration to work with async.series():
function loopThroughDetails(info, projID, i, callback) {
if (i < 500) {
getProjectDetails(projID + "-" + i, function (finished) {
if (JSON.stringify(finished) == "[]") {
info.ProjID = projID + "-" + i;
DB_COLLECTION_NAME.insert(info, function (err, result) {
assert.equal(err, null);
callback();
});
} else {
i++;
loopThroughDetails(info, projID, i, callback);
}
});
}
}
And after calling all this, I simply use async.series to accomplish the task:
async.series(queue, function () {
console.log('all done');
});
What am I doing wrong here? Thanks so much for any help you can give! :)
Firstly, there are many methods to achieve what you are looking for and most are subjective. I like to use an array.shift method when iterating synchronously when possible. The concept goes something like this.
// say you have an array of projects you need to add.
var arrayOfProjects = [{name: "project1"}, {name: "project2"}, {name: "project3"}];
// This takes the first project off of the array and assigns it to "next" leaving the remaining items on the array.
var nextProject = function (array) {
// if there are items left then do work. Otherwise done.
if (array.length > 0) {
// shift the item off of the array and onto "next"
var next = array.shift();
addNewProject(next);
}
}
var addNewProject = function (project) {
// Do stuff with the project
console.log("project name: ", project.name);
// When complete start over
nextProject(arrayOfProjects);
}
// Start the process
nextProject(arrayOfProjects);
Here is a working Example
if you inspect the page you will see the projects logged to the console in order.
I have a situation where i am running a jquery function and in that function i have an each loop. The each loop takes some time for processing and that is why the next statement executes before each completes. This creates a problem for me. I want a function to execute when each completes. My function is as follows:-
function myFunc() {
// Do something
$.each(mylist, function (i, val) {
// do something
filepicker.store(myList, function (stored_fpfile) {
console.log("Store successful:", JSON.stringify(stored_fpfile));
}, function (FPError) {
// Error
}, function (progress) {
console.log("Loading: " + progress + "%");
}
);
});
CallMyFunction();
}
Call my function executes before each loop finishes.
I dont want to use count of the list to detect and run my procedure. I want a reliable solution.
I am using the InkFilePicker API to store files to Amazon
Any help is appreciated.
I would suggest generating deferred objects for each iteration that are then stored in an array. Then, after the each, you can wait until all those deferred objects are complete to run your other function.
function myFunc() {
// Do something
var promiseArray = $.map(mylist, function (i, val) {
return $.Deferred(function(def){
// do something
filepicker.store(val, function (stored_fpfile) {
def.resolve(stored_fpfile);
}, function (FPError) {
def.reject(FPError);
}, function (progress) {
def.notify(progress); // won't actually be accurate
});
}).promise();
});
$.when.apply($,promiseArray).done(function(){
console.log(arguments); // array of results from onSuccess callbacks
CallMyFunction();
})
}
Apparently, the store is a wrapper around an AJAX object. You should test the progress and when it is completed call CallMyFunction, like so before the call to each:
items = myList.length;
And inside each:
...
function (progress) {
console.log("Loading: " + progress + "%");
if (--items === 0)
{
CallMyFunction();
}
}
...
There aren't any counter arguments, performance is not impacted by this, you're sending data over the Internet which is the real bottleneck.
This is also reliable. In case of error you should decrement with --items too.
Hi i am using async module of node.js for implementing a for loop asynchronously.
My question is: how to break the loop execution and get out of the loop? I tried giving return , return false but no luck.
Here is the sample code:
async.until(
function() { return goal; },
function(callback) {
async.each(_rules,
function(rule,callback) {
var outcome = true;
.... some code ....
if(changes){
console.log("hi");
return false;// HERE I NEED TO BREAK
}
else
callback();
},
function(err){ }
);
if (!changes || session.process)
goal = true;
callback();
},
function(err){ callback(session); }
);
async.until repeatedly calls function until the test returns true. So test must return true so that you exit the loop. This is opposite of async.whilst which runs repeatedly while test evaluates to be true.
async.each calls the functions in parallel so what it returns does not matter. It is not a loop which you can break, but an iterator looping over the array. Your condition to stop using async.each should be in test for async.until and you should iterate over the rules yourself.
There isn't really a "loop" as such to break out of. All your items in your collection are used in parallel
The only way to "break" the "loop" is to call the callback with an error argument. As there is nothing to stop you from putting other things in there you could hack it a little bit.
From the docs:
Note, that since this function applies the iterator to each item in
parallel there is no guarantee that the iterator functions will
complete in order.
Even if you return an error, you will still have several outstanding requests potentially so you really want to limit the amount of items you use in one go. To limit the amount of outstanding requests, you could use eachSeries or eachLimit.
For example:
async.each(_rules,
function(rule,callback) {
if(changes){
return callback({ data: 'hi'}); // stop
}
...
if(realerror){
return callback({ error: realerror}); // stop with an error
}
callback(); // continue
},
function(result){
if(!result){
return othercallback('no results');
}
// check if we have a real error:
if(result.error){
return othercallback(result.error);
}
return othercallback(null, result.data);
}
);
PS: if you're not doing async, use underscore
You have also async.detect
Returns the first value in coll that passes an async truth test. The iteratee is applied in parallel, meaning the first iteratee to return true will fire the detect callback with that result.
// asynchronous function that checks if a file exists
function fileExists(file, callback) {
fs.access(file, fs.constants.F_OK, (err) => {
callback(null, !err);
});
}
async.detect(['file3.txt','file2.txt','dir1/file1.txt'], fileExists,
function(err, result) {
console.log(result);
// dir1/file1.txt
// result now equals the first file in the list that exists
}
);
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