I have a recursive function and I am trying to make it run in sequence, returning the promise every time.
The code is recursive and works well but works only for the first item in the for loop.
Example:
- folder: 1 - OK
- folder: 11 - OK
- folder: 111 - OK
- folder: 111 - OK
- folder: 2 - NOT OK
- folder: 12 - NOT OK
- All asynchronous downloads completed in sequence.
I think it is because when I return the promise from inside, then the for loop gets interrupted.
function CopySubFolders(folder, destFolder) {
// Recursively copy each subfolder
return folder.getFoldersAsync()
.then(function (folderlist) {
if (folderlist.size > 0) {
for (var i in folderlist) {
var replace = Windows.Storage.CreationCollisionOption.replaceExisting;
console.log("create folder: " + folderlist[i].name);
return destFolder.createFolderAsync(folderlist[i].name, replace)
.then(function (newdest) {
return CopySubFolders(folderlist[i], newdest);
});
}
}
else {
return WinJS.Promise.as();
}
});
}
CopySubFolders(folder, self.localFolder)
.then(function () {
completeFunc("Done");
console.log("All asynchronous downloads completed in sequence.");
})
Any idea how to return the promise without interrupting the for loop?
PS: if I use a forEach loo it will not get interrupted, but then I loose the capability of returning the folders in sequence.
Example:
- folder: 1 - OK
- All asynchronous downloads completed in sequence.
- folder: 11 - OK
- folder: 111 - OK
- folder: 111 - OK
- folder: 2 - OK
- folder: 12 - OK
Yes, just like with any function, if you execute a return
statement, the function will stop what it's doing and return. You should be able to accomplish what you're trying to do with the following:
Edit: if you don't need them to complete in a specific sequence, you can accomplish what you are trying to do with WinJS.Promise.join()
(a.k.a. Promise.all()
in other promise dialects) and map
(I'm factoring out the inner part here to cut down on the nesting):
function CopySubFolders(folder, destFolder) {
return folder.getFoldersAsync()
.then(function (folderlist) {
return WinJS.Promise.join(folderlist.map(function (folder) {
return CopyFolder(folder, destFolder);
});
});
}
function CopyFolder(folder, destFolder) {
var replace = Windows.Storage.CreationCollisionOption.replaceExisting;
console.log("create folder: " + folder.name);
return destFolder.createFolderAsync(folder.name, replace)
.then(function (newdest) {
return CopySubFolders(folder, newdest);
});
}
As a complete side note, please don't use for...in
with arrays. It's a bad idea.
And as a bit of a vestige of this post, here is how you would be able to create the folders in sequence if you needed to (though that is not recommended):
function CopySubFolders(folder, destFolder) {
var p = Promise.resolve();
return folder.getFoldersAsync()
.then(function (folderlist) {
return folderlist.forEach(function (folder) {
var replace = Windows.Storage.CreationCollisionOption.replaceExisting;
console.log("create folder: " + folder.name);
p = p.then(function () {
destFolder.createFolderAsync(folder.name, replace)
.then(function (newdest) {
return CopySubFolders(folder, newdest);
});
});
});
});
return p;
}
Another slightly cleaner way to do the same thing, as illustrated here, is to use folderlist.reduce()
:
function CopySubFolders(folder, destFolder) {
return folder.getFoldersAsync()
.then(function (folderlist) {
return folderlist.reduce(function (sequence, folder) {
var replace = Windows.Storage.CreationCollisionOption.replaceExisting;
console.log("create folder: " + folder.name);
return sequence.then(function () {
destFolder.createFolderAsync(folder.name, replace)
.then(function (newdest) {
return CopySubFolders(folder, newdest);
});
});
}, Promise.resolve());
});
}