我正在组合一个示例,说明如何将简单的同步Node.js程序转换为使用async / await的异步版本。应该有几个中间步骤,从一个普通的基于回调的版本开始,跟进一个使用两个回调的步骤,一个用于正常(解决)情况,另一个用于错误(拒绝)情况,这将导致承诺。
每个版本的工作是创建一个空文件夹 copy (可能已经存在并且可能包含文件)并复制所有文件(称为 file1.txt 和 file2.txt )在 orig 文件夹中。如果在应该明确捕获的任何地方发生错误,则打印到控制台并且程序不应再继续。
具有正常错误优先回调的版本工作正常,但我遇到了拆分回调版本的问题。它只复制file2.txt,但不复制file1.txt。
这是我用来转换fs函数的代码:
const fs = require('fs');
fs.exists = function(path, callback) {
fs.stat(path, (err, stats) => {
if (err) {
callback(null, false);
} else {
callback(null, true);
}
});
};
function splitCallback(f) {
return (...params) => {
reject = params[params.length - 2];
resolve = params[params.length - 1];
params = params.slice(0, params.length - 2);
f(...params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
};
}
const sfs = {};
const functionNames = ['exists', 'readdir', 'unlink', 'mkdir', 'readFile', 'writeFile'];
for (const functionName of functionNames) {
sfs[functionName] = splitCallback(fs[functionName].bind(fs));
}
这是使用这些功能的实际示例:
function handleError(err) {
console.error(err);
}
function initCopyDirectory(callback) {
sfs.exists('copy', handleError, exists => {
if (exists) {
sfs.readdir('copy', handleError, filenames => {
let fileCount = filenames.length;
if (fileCount === 0) {
callback();
}
for (const filename of filenames) {
sfs.unlink(`copy/${filename}`, handleError, () => {
fileCount--;
if (fileCount === 0) {
callback();
}
});
}
});
} else {
sfs.mkdir('copy', handleError, () => callback);
}
});
}
function copyFiles() {
// sfs.readdir('orig', handleError, filenames => {
// for (const filename of filenames) {
// console.log(filename);
// sfs.readFile(`orig/${filename}`, handleError, data => {
// console.log('reading', filename);
// sfs.writeFile(`copy/${filename}`, data, handleError, () => {
// console.log('writing', filename);
// });
// });
// }
// });
sfs.readdir('orig', handleError, filenames => {
for (const filename of filenames) {
fs.readFile(`orig/${filename}`, (err, data) => {
if (err) {
handleError(err);
} else {
sfs.writeFile(`copy/${filename}`, data, handleError, () => {});
}
});
}
});
}
function main() {
initCopyDirectory(copyFiles);
}
main();
正如它在这里写的那样它可以正常工作(使用Node版本7.4.0 for Windows),但是当我在copyFiles-function中交换注释(从而改变readFile)时,只复制了一个文件,我得到以下输出:
file1.txt
file2.txt
reading file2.txt
writing file2.txt
writing file2.txt
有什么问题?
答案 0 :(得分:0)
尝试使用此代替注释代码:
for (const filename of filenames) {
(function(filename){
console.log(filename);
sfs.readFile(`orig/${filename}`, handleError, data => {
console.log('reading', filename);
sfs.writeFile(`copy/${filename}`, data, handleError, () => {
console.log('writing', filename);
});
});
})(filename)
}
问题是你在for循环中运行异步函数并期望它们同步运行。在调用sfs.writeFile
时(执行sfs.readFile
之后),for循环早已完成执行,因此只剩下最后一个文件名file2。通过将for循环中的所有内容包装在闭包中,可以保持正确的值。
这是一个更简单的例子:
for (var i = 0; i < 10 ; i++) {
setTimeout(function(){
console.log(i)
}, 100)
}
将打印10
10次,因为在超时执行时(0.1秒)for循环已经完成,而下面的代码将打印数字0到9,因为原始值由闭包保留。 (亲自尝试)
for (var i = 0; i < 10 ; i++) {
(function(i){
setTimeout(function(){
console.log(i)
}, 100)
})(i)
}
答案 1 :(得分:0)
问题是我忘了将const放在splitCallback中的变量声明前面。这使得它们成为可以被覆盖的全局变量。激活严格模式会引发错误。这是正确的代码:
public List<Person> rank(List<Person> people) {
return people
.stream()
.sorted(Comparator.<Person>comparingDouble(x -> x.profit).reversed())
.collect(new IntegerRankingCollector<>(
Comparator.comparingDouble(p -> p.profit), // how to differentiate rankings
p -> p.rank, // where to get rank for an element which was already ranked
(p, rank) -> new Person(rank, p.profit) // how to create an element from another element values and a rank
));
}