The following code is responsible for reading files. My requirement is how to find whether all files has been read so that I can return or resolve a promise from the parent fun
What if I change the code structure to this
$.when(readmultifiles(files)).then(
function(status) {
alert(status + ", things are going well");
},
function(status) {
alert(status + ", you fail this time");
},
function(status) {
$("body").append(status);
}
);
function readmultifiles(files) {
var dfrd = $.Deferred();
// Read first file
setup_reader(files, 0);
function setup_reader(files, i) {
var file = files[i];
var name = file.name;
var reader = new FileReader();
reader.onload = function(e) {
readerLoaded(e, files, i, name);
};
reader.readAsBinaryString(file);
// After reading, read the next file.
}
function readerLoaded(e, files, i, name) {
// get file content
var bin = e.target.result;
// do sth with text
namee.push(name);
// If there's a file left to load
if (i < files.length - 1) {
// Load the next file
setup_reader(files, i + 1);
} else {
dfrd.resolve(namee.join(','));
}
}
return dfrd.promise();
}
There are several things to consider in a good design using promises that your implementation could learn from:
readFile()
. It also makes readFile()
more useful elsewhere in your project or in future projects.Given all that, here are five ways to implement your code -using standard promises, using jQuery promises and with your operation sequences or run in parallel and using Bluebird promises. In all cases, you get an array of results in order at the end.
Promisify readFile()
using standard promises
First, let's "promisify" your readFile operation so you can then use promise logic to control things.
function readFile(file) {
return new Promise(function(resolve, reject) {
var reader = new FileReader();
reader.onload = function(e) {
resolve(e.target.result);
};
reader.onerror = reader.onabort = reject;
reader.readAsBinaryString(file);
});
}
With standard promises, all operation in parallel
To run all your file operations in parallel and return all the results in order and use standard promises, you can do this:
function readmultifiles(files) {
return Promise.all(files.map(readFile));
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});
With standard promises, all operations in sequence
To run all your files operations in sequence (which it does not look like you need to do here because all the operations are indepedent even though your original code was sequencing them) and return all the results in order and use standard promises, you can do this.
This, somewhat standard design pattern for sequencing uses .reduce()
to sequence through the array and chain all the operations together so they are run one at a time down the sequence of the chain:
function readmultifiles(files) {
var results = [];
files.reduce(function(p, file) {
return p.then(function() {
return readFile(file).then(function(data) {
// put this result into the results array
results.push(data);
});
});
}, Promise.resolve()).then(function() {
// make final resolved value be the results array
return results;
});
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});
And, here's how it would look using jQuery promises
Promisify readFile()
using jQuery promises:
function readFile(file) {
return new $.Deferred(function(def) {
var reader = new FileReader();
reader.onload = function() {
def.resolve(e.target.result);
};
reader.onerror = reader.onabort = def.reject;
reader.readAsBinaryString(file);
}).promise();
}
Run in a parallel with jQuery:
function readmultifiles(files) {
return $.when.apply($, files.map(readFile));
}
// sample usage
readmultifiles(arrayOfFiles).then(function() {
var results = Array.prototype.slice(arguments);
// all results in the results array here
});
And, to run in sequence with jQuery
function readmultifiles(files) {
var results = [];
files.reduce(function(p, file) {
return p.then(function() {
return readFile(file).then(function(data) {
// put this result into the results array
results.push(data);
});
});
}, $.Deferred().resolve()).then(function() {
// make final resolved value be the results array
return results;
});
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});
Bluebird implementation
And, for completeness, I'll show you what it looks like using a little more advanced promise library like Bluebird that has additional capabilities that are useful here. The parallel code and the implementation of readFile()
is the same as for standard promises, but for the sequential implementation, it could take advantage of some built-in Bluebird operations for sequencing async operation and it would just consist of:
function readmultifiles(files) {
return Promise.mapSeries(files, readFile);
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});