The function below prints Chrome bookmarks in a folder recursively. How could I alter the below function to call another function after the final recursive loop is processed? chrome.bookmarks.getChildren() is asynchronous which makes it difficult to know when the function is done processing everything.
Thanks.
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i]);
}
// I'd like any code here to be run only after the above has
//finished processing
function printBookmarks(id) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id);
});
});
}
EDIT: Sorry, I don't think I was clear in the initial code example. I've updated the code to show the problem I'm having with the asynchronous function by calling the function multiple times. I'd like any code after the printBookmarks function calls to wait for all the printBookmarks functions to finish processing.
Your asynchronous method instances may all be executing at once, and you don't know how many there will be beforehand. So, you'll have to keep count and then use a callback when the last asynchronous method is done.
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i], thingsToDoAfter);
}
function thingsToDoAfter() {
// I'd like any code here to be run only after the above has
// finished processing.
}
var count = 0;
function printBookmarks(id, callback) {
count++;
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id, callback);
});
count--;
if (count === 0 && callback)
callback();
});
}
I've recently had to solve this problem. The solution was similar to Eric's, but I found that I needed the count variable to be local to the function. Here's how I would solve this:
for(var i=0;i<foldersArray.length; i++){
// The loop make's several call's with different folder ID's.
var printed_children = 0;
printBookmarks(foldersArray[i],function() {
printed_children++;
if(printed_children == foldersArray.length){
// You know you are done!
}
});
}
// I'd like any code here to be run only after the above has
//finished processing.
function printBookmarks(id,finished_callback) {
// the printed_children count is local so that it can keep track of
// whether or not this level of recursion is complete and should call
// back to the previous level
var printed_children = 0;
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
// added a callback function to the printBookmarks so that it can
// check to see if all upstream recursions have completed.
printBookmarks(bookmark.id,function() {
printed_children++;
if(printed_children == children.length){
finished_callback();
}
});
});
if(children.length == 0){
finished_callback();
}
});
}
It's a bit ugly, but it should work.
You could save all your completed calls into a variable and test against the number of bookmarks you want to process. When you reach the end (the count of completions equals the amount of bookmarks to process), then you execute your final function.
An answer to a similar problem is here, with code that you can use as a guide:
How do I load a variable number of scripts with jQuery.getScript() before running javascript code?
you can do something like this JQFAQ.com.I'm updating for the future uses.
Might be a better way to go about this, but you could add a depth parameter, something like
printBookmarks('0', 0);
function printBookmarks(id, depth) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id, depth + 1);
});
if(depth == 0) yourFunction();
});
}
EDIT in response to comment
This is a variation on another answer for a slightly different approach.
runCount = 0;
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i]);
runCount++;
}
while(runCount > 0) { // sleep for 10ms or whatnot}
// I'd like any code here to be run only after the above has
// finished processing.
function printBookmarks(id) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id);
runCount--;
});
});
}
Related
I'm trying to do several calls inside a loop to an asynchronous API until the value that I want (true in this case) is returned. The issue is that I don't want to keep looping after that value is found and I don't want to execute the lines after the loop without the value returned from it...but so far I can't get that working. I think I'm doing something wrong here, but "I can't get the whole picture".
function isSelected(element, callback) {
// This is a little bit confusing for me...can I just get the value from
// getDataAsync without using isSelected function?
Office.select(element).getDataAsync(function (asyncResult) {
var result = true;
// some logic here that may change 'result'
callback(result);
});
}
function delete(elements) {
var index = -1;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
isSelected(elements[i], function(result) {
if (result) { index = i; }; // ...and I don't want to keep "looping"
});
}
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
Have you tried Kriskowal's Q? There's a nice function called Q#allSettled:
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
So basically this is how it would work in your case:
var promises = [];
for(/* conditions */) {
promises.push(/* async call which returns a promise */);
}
Q.allSettled(promises).then(function(results) {
results.forEach(function (result) {
var value;
if (result.state === "fulfilled") {
value = result.value;
// do something with "value"
}
});
});
allSettled just makes sure the then will be executed regardless of whether or not the promise was successful or not, and you can check the value of the object you retrieve from your async call.
I am recommending three ways to doing it.
Using just JavaScript.
Using Async library.
Using underscore library.
Here you can see the javascript implementation:
You can do something like that:
You need to track home many times, you call the function, and how many times the callback happened
function delete(elements) {
var index = -1;
var stack=0;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
stack++ // Go up on each loop
isSelected(elements[i], function() {
stack--; //Go down each callback
index = i;
if(stack==0) afterAllFinish() //When it return to 0 mean all callback have finished
});
}
function afterAllFinish(){
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
}
Using other libraries:
Please take a look at http://underscorejs.org/#after for the underscore way to solve it.
Please take a look at https://github.com/caolan/async#parallel to see the async way to solve it.
I need to retrieve several values from an IndexedDB, check if all of them fulfill some constraint (different for all of them) and if so call a function. To illustrate this better imagine that calls to IndexedDB were sychronous, we would have something like.
function myFunc(varNames, conditions) {
for(var i = 0; i < varNames.length; i++) {
if(!isGood(db.get(varNames[i]), conditions[i])) {
return;
}
}
doStuff();
}
Since IndexedDB is are asynchronous I do not konw how to do it. Using a callback in every get is not really feasible since the call to doStuff depends on all the gets. Accumulating the results in a global variable would not work either because myFunc is called more than one. I tried something like:
function myFunc(varNames, conditions) {
var valid = 0;
checker() {
valid++;
if(valid == varNames.length) {
doStuff();
}
}
for(var i = 0; i < varNames.length; i++) {
db.get(varNames[i], function(val) {
if(isGood(val, conditions[i]) {
checker();
}
});
}
}
But that does not seems to work either. Any ideas?
You can make the DB calls one at a time, and use the success callback to make the next DB call. Something like this:
function myFunc(varNames, conditions){
if(varNames.length){
var name = varNames.shift(),
condition = conditions.shift();
db.get(name, function(val){
if(isGood(val, condition)){
myFunc(varNames, conditions);
}
});
} else {
doStuff();
}
}
How do I make it so my server doesn't get stuck doing the for loop and can handle other requests? It would be nice if I could do the for loop in parallel while my server does other stuff.
socket.on('drawing', function() {
for (var i = 0; i < x.length-1; i++)
{
//do stuff
}
});
Async.js has a bunch of helper functions like foreach and whilst that do exactly what you're asking for here.
Edit:
Here's a full example:
async = require('async');
var i = 0
async.whilst(
// test
function() { return i < 5; },
// loop
function(callback) {
i++;
console.log('doing stuff.');
process.nextTick(callback);
},
// done
function (err) {
console.log('done!');
}
);
Which outputs:
doing stuff.
doing stuff.
doing stuff.
doing stuff.
done!
Edit 2: Changed named functions to comments to avoid confusing people.
You can use process.nextTick to move functionality away, but it wont be truly parallel, Node.js is an event driven language so it doesn't really do threads. Here's an overview of using process.nextTick.
Based on #Slace's answer, you can use asynchronous recursion.
socket.on('drawing', function {
var i = 0;
(function someFunction() {
if (i < x.length - 1) {
i++;
// Do stuff.
process.nextTick(someFunction)
} else {
// Do something else.
}
}());
});
I have something like this:
for (var i=0;i<result.qry.ROWCOUNT;i++) {
myAsync(i);
}
How do I know when all my Async functions have finished executing?
At the risk of someone replying with "Needs more jQuery!", can I use the jQuery promise object? Or deferred or something like that?
Keep track of how many asynchronous calls are outstanding. When each finishes, decrement your counter. When you get to 0, you are in the last callback.
var asyncsLeft = 0;
for (var i=0;i<10;++i){
asyncsLeft++;
doSomethingAsyncWithCallback(function(){
// This should be called when each asynchronous item is complete
if (--asyncsLeft==0){
// This is the last one!
}
});
}
Due to the single-threaded nature of JavaScript there is no potential race condition where you might get your callback invoked before all of the asynchronous calls have been queued up. It is safe to put the asyncsLeft++ call after the doSomethingAsynchronous, if you like.
This is how I would do it:
//Do stuff up here to get records
var rowCount = result.qry.ROWCOUNT, //Save the row count
asyncCount = 0, //The count of complete async calls
asyncCallback = function() {
//To be called whenever an async operation finishes
asyncCount++; //Increment the row counter
if (asyncCount >= rowCount) {
//Do stuff when they're all finished
}
};
for (var i=0;i<rowCount;i++) {
myAsync(i, asyncCallback);
}
function myAsync(index, completeCallback) {
//Do async stuff with index
//Call completeCallback when async stuff has finished or pass it
// into the async function to be called
}
In jQuery, there is the $.ajaxStop function that runs after the last Ajax has ran.
If you are using jQuery, you can also use the ajaxSend and ajaxComplete methods to keep your counter code separate from your dispatch code.
var ajaxPending = 0;
function ajax_changed(indicator, pending) {
if (pending)
$(indicator).show();
else
$(indicator).hide();
}
$('#loading-indicator').ajaxSend(function() {
ajax_changed(this, ++ajaxPending);
});
$('#loading-indicator').ajaxComplete(function() {
ajax_changed(this, --ajaxPending);
});
Use a callback function:
for (var i=0;i<result.qry.ROWCOUNT;i++) {
myAsync(i, myCallback);
}
function myCallback(i){
//set result.qry.ROWCOUNT to a var somewhere above if it's not available in this scope
if(i == (result.qry.ROWCOUNT - 1)){
//now you know you're actually done with all requests
}
}
function myAsync(i,callback){
///do work
callback(i);
}
The function below prints Chrome bookmarks in a folder recursively. How could I alter the below function to call another function after the final recursive loop is processed? chrome.bookmarks.getChildren() is asynchronous which makes it difficult to know when the function is done processing everything.
Thanks.
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i]);
}
// I'd like any code here to be run only after the above has
//finished processing
function printBookmarks(id) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id);
});
});
}
EDIT: Sorry, I don't think I was clear in the initial code example. I've updated the code to show the problem I'm having with the asynchronous function by calling the function multiple times. I'd like any code after the printBookmarks function calls to wait for all the printBookmarks functions to finish processing.
Your asynchronous method instances may all be executing at once, and you don't know how many there will be beforehand. So, you'll have to keep count and then use a callback when the last asynchronous method is done.
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i], thingsToDoAfter);
}
function thingsToDoAfter() {
// I'd like any code here to be run only after the above has
// finished processing.
}
var count = 0;
function printBookmarks(id, callback) {
count++;
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id, callback);
});
count--;
if (count === 0 && callback)
callback();
});
}
I've recently had to solve this problem. The solution was similar to Eric's, but I found that I needed the count variable to be local to the function. Here's how I would solve this:
for(var i=0;i<foldersArray.length; i++){
// The loop make's several call's with different folder ID's.
var printed_children = 0;
printBookmarks(foldersArray[i],function() {
printed_children++;
if(printed_children == foldersArray.length){
// You know you are done!
}
});
}
// I'd like any code here to be run only after the above has
//finished processing.
function printBookmarks(id,finished_callback) {
// the printed_children count is local so that it can keep track of
// whether or not this level of recursion is complete and should call
// back to the previous level
var printed_children = 0;
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
// added a callback function to the printBookmarks so that it can
// check to see if all upstream recursions have completed.
printBookmarks(bookmark.id,function() {
printed_children++;
if(printed_children == children.length){
finished_callback();
}
});
});
if(children.length == 0){
finished_callback();
}
});
}
It's a bit ugly, but it should work.
You could save all your completed calls into a variable and test against the number of bookmarks you want to process. When you reach the end (the count of completions equals the amount of bookmarks to process), then you execute your final function.
An answer to a similar problem is here, with code that you can use as a guide:
How do I load a variable number of scripts with jQuery.getScript() before running javascript code?
you can do something like this JQFAQ.com.I'm updating for the future uses.
Might be a better way to go about this, but you could add a depth parameter, something like
printBookmarks('0', 0);
function printBookmarks(id, depth) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id, depth + 1);
});
if(depth == 0) yourFunction();
});
}
EDIT in response to comment
This is a variation on another answer for a slightly different approach.
runCount = 0;
for (var i = 0; i < foldersArray.length; i++) {
// The loop makes several calls with different folder IDs.
printBookmarks(foldersArray[i]);
runCount++;
}
while(runCount > 0) { // sleep for 10ms or whatnot}
// I'd like any code here to be run only after the above has
// finished processing.
function printBookmarks(id) {
chrome.bookmarks.getChildren(id, function(children) {
children.forEach(function(bookmark) {
console.debug(bookmark.title);
printBookmarks(bookmark.id);
runCount--;
});
});
}