以下代码负责读取文件.我的要求是如何查找是否已读取所有文件,以便可以从父函数(readmultifiles)返回或解决承诺.
$.when(readmultifiles(files))
.then(function(){//all files uploaded}))
Above code initiates the file read. What can be done so that upon reading of all files
callback is done or a return can be made.
function readmultifiles(files) {
// 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
// If there's a file left to load
if (i < files.length - 1) {
// Load the next file
setup_reader(files, i + 1);
}
}
解决方法:
在一个好的设计中,应考虑到您的实现可以从中学习的一些事项:
>从最低级别的异步操作中创建承诺(称为“承诺”).然后,您可以使用promise功能来控制逻辑流并传播错误,并且可以用promise一致地实现您的代码.在这种情况下,这意味着您应该保证readFile()正确.它还使readFile()在您的项目中或将来的项目中的其他地方更加有用.
>确保您始终正确地传播错误.在不使用promise的情况下使用异步代码,很难正确地将错误返回给原始调用者,尤其是如果异步逻辑最终变得复杂(使用嵌套或序列操作)时.
>请仔细考虑您的异步操作是否必须是序列或它们是否可以并行运行.如果一个操作不依赖于另一个操作,并且您不太可能因多个请求而使某些服务过载,那么并行运行事物通常会更快地获得结果.
>通过异步函数返回承诺,以便调用者可以知道何时完成操作并可以访问异步结果.
>不要不必要地围绕现有的承诺创建另一个承诺(被视为承诺反模式之一).
>如果使用jQuery Promise,请尝试坚持与Promise标准兼容的jQuery功能,以免将来遇到互操作性问题,也不会使将来的代码读者更可能知道标准Promise如何工作.
有了所有这些,这里有五种方法来实现您的代码-使用标准的Promise,使用jQuery Promise和您的操作序列或并行运行以及使用Bluebird Promise.在所有情况下,最后都将获得一系列结果.
使用标准promisise readFile()
首先,让我们“承诺”您的readFile操作,以便随后可以使用promise逻辑来控制事物.
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);
});
}
遵循标准承诺,所有操作并行
要并行运行所有文件操作并按顺序返回所有结果并使用标准的Promise,可以执行以下操作:
function readmultifiles(files) {
return Promise.all(files.map(readFile));
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});
遵循标准承诺,所有操作按顺序进行
要按顺序运行所有文件操作(尽管您的原始代码对它们进行了排序,但似乎所有操作都是独立的,因此似乎不需要执行此操作)并按顺序返回所有结果并使用标准的Promise.这个.
这种用于排序的标准设计模式使用.reduce()遍历数组并将所有操作链接在一起,以便它们在链的顺序中一次运行一次:
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
});
而且,这是使用jQuery Promise的外观
使用jQuery Promisise readFile():
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();
}
与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
});
并且,要与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
});
蓝鸟实施
而且,为完整起见,我将向您展示使用more advanced promise library like Bluebird小巧的外观,其中包含一些有用的附加功能.并行代码和readFile()的实现与标准Promise相同,但是对于顺序实现,它可以利用一些内置的Bluebird操作对异步操作进行排序,并且它仅包括:
function readmultifiles(files) {
return Promise.mapSeries(files, readFile);
}
// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
// all results in the results array here
});