将节点式回调拆分为两个回调

时间:2017-01-10 11:42:19

标签: javascript node.js asynchronous callback ecmascript-6

我正在组合一个示例,说明如何将简单的同步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

有什么问题?

2 个答案:

答案 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
                ));
    }