I want to iterate through a list and rename a few files in Windows 8 / Javascript. Therefore, I wrote a function called "renameFile" and I call this function within a loop, like this:
list.forEach(function (value, index, array) {
var filename = index;
var newfilename = index+1;
renameFile(filename, newfilename);
});
function renameFile(filename, newfilename) {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync(filename).then(function (sampleFile) {
sampleFile.renameAsync(newfilename).done(
function complete(result) {
},
function error(error) {
console.log("error" + error);
}
);
});
}
The problem is: The rename function is asynchronous and it seems that sometimes, the renaming works and sometimes not: In most cases, only the first element of the list is renamed. How can I tell the loop to wait, till the rename-process of the first item in the list is finished and then go an with the second, third, ... item?
Cheers
Well I did a fast lookup and you're out of luck. It seems like the StorageFolder class only has Asyn functions for what you're looking for. But it's not the end.
The easiest solution I have to do use a recursive function. And call the function with a different index.
function renameFile(filename, newfilename, list, index) {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync(filename).then(function (sampleFile) {
sampleFile.renameAsync(newfilename).done(
function complete(result) {
renameList(list, index);
},
function error(error) {
console.log("error" + error);
}
);
});
function renameList(list, index) {
if(index >= list.length) return;
renameFile(index, index+1, list, index+1);
}
renameList(list, 0);
It's not very clean but it should work, this will force the code to be synchrone since you're calling from within a callback.
Related
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.
I have the following code which runs in a loop:
var index = fileNames[x].lastIndexOf("/") + 1;
var currentImageName = fileNames[x].substr(index);
if (currentImageName.indexOf(".jpg") != -1) {
reader.getFileAsBlob(fileNames[x])
.done(function(blob) {
picturesFilePathArray.push({
fileName: currentImageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
}
The problem I am having is that I am trying to save an object with 2 properties into an array. This object should have the identifier and the result. (fileName and fileURL respectively). But since this function is asynchronous (executed through a promise). By the time the "getFileAsBlob" finishes, currentImageName has already been updated ultimately ending in many of my objects having the same identifier (the last one processed before it finished).
This might be a really easy problem, but I am very new to javascript and haven't yet found anything about it.
I thought that the solution might be in passing the variable to the "done" function, but I think this function is the one being returned by the method and is already set. (I dont know how it looks)
Edit:
The code is just inside a normal loop
for (x = 0; x<fileNames.length; x++)
So create a function so the variable can not be changed
function getFile (filmName, imageName) {
reader.getFileAsBlob(fileName)
.done(function(blob) {
picturesFilePathArray.push({
fileName: imageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
}
and call it
if (currentImageName.indexOf(".jpg") != -1) {
getFile (fileNames[x], currentImageName);
}
or you can do something like
if (currentImageName.indexOf(".jpg") != -1) {
(function (fileName, imageName) {
reader.getFileAsBlob(fileName)
.done(function(blob) {
picturesFilePathArray.push({
fileName: imageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
})(fileNames[x], currentImageName);
}
MDN Closure
The solution to this problem is always the same: Use a closure.
But since you are using a promise based library, you have a nicer option. Use promises. (Internally this is based on closures as well, of course. It's just a much nicer abstraction.)
function getFileInfo(path) {
return reader.getFileAsBlob(path).done(function (blob) {
return {
fileName: path.split('/').pop(),
fileURL: blobURL(blob)
});
};
}
function isJpg(filename) {
return /\.jpg$/i.test(filename);
}
Now you can do this, where refreshKMZList() is called once per file:
fileNames.filter(isJpg).forEach(function (path) {
getFileInfo(path).then(function (fileInfo) {
picturesFilePathArray.push(fileInfo);
refreshKMZList();
})
.catch(function (error) {
// handle the error
});
});
or even this, where refreshKMZList() is called only once per overall:
var fileInfos = fileNames.filter(isJpg).map(getFileInfo);
Promise.all(fileInfos).then(function (arrayOfFileInfo) {
picturesFilePathArray.concat(arrayOfFileInfo);
refreshKMZList();
})
.catch(function (error) {
// handle the error
});
Read up on promises, they are worth being understood.
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
Am currently working on a windows store javascript application and stuck at implementing the asynchronous for loop.
Consider a simple for loop as follows
for(i=0,i<4,i++)
{
//body
}
What will be the exact asyncFor loop that can execute the same process as the above code
Use callback functions.
someFunction = function(data, ...){
someAsyncFn(someFunction); //use this function as the callback
}
Only if I understood you correct... You coud try something like this:
function asyncFor(n, callback)
{
var i=0;
var t=setInterval(function(){
callback();
i++;
if(i==n)
clearInterval(t);
}, 0);
}
asyncFor(4, function(){
console.log('hello world');
});
console.log('after for');
If not - sorry and ignore my answer
You can use a recursive function callback
function test(){
myCall(function(){
alert("Hello world");
},5);
}
function myCall(callback , count){
if(count === 0){
return;
}else{
callback();
count--;
}
myCall(callback , count);
}
Usually, you need to code the loop recursively - invoking the next recursion step from the asynchronous callback:
function loop(i, n, body, callback) {
if (i < n)
body(i, function cb() {
loop(i+1, n, body, callback);
});
else
callback();
}
Then use it like
loop(0, 4, function(i, cb) {
console.log(i);
setTimeout(cb, 50); // or whatever async task with callback you have
}, function() {
console.log("done everything");
});
If you're looping over an array, these two helpers may come in handy (CoffeeScript syntax for more succinct notation). Feel free to convert them to standalone functions if you're uncomfortable with monkey-patching.
# call function for each element in array
# returns when all promises have been completed
Array::eachAsync = (fn) ->
promises = #map (item, index, collection) -> fn(item, index, collection)
WinJS.Promise.join(promises)
# call function for each element in array in order
# returns when the last one has been processed
Array::sequentialEachAsync = (fn) ->
#reduce (lastPromise, item) ->
lastPromise.then () -> fn(item)
, WinJS.Promise.wrap()