【问题标题】:Node.js how to iterate with async callback?Node.js 如何使用异步回调进行迭代?
【发布时间】:2016-06-01 13:04:18
【问题描述】:

我正在做的是使用 fs 将 5 个 html 页面部分(html, head, chead, topaside, main, footer)拼接在一起。文件名是htmlpage.js,所以你可以在命令行工具中运行node htmlpage.js file1 file2 file3 ...,它将那些html页面部分拼接在一起,然后吐出file1.html, file2.html, file3.html ...。我不喜欢使用模板引擎/库/框架之类的东西,尤其是在我学习的时候。
这是源代码:

'use strict';
const fs = require('fs'),
      head = fs.createReadStream('./html-parts/head.html', 'utf8'),
      topaside = fs.createReadStream('./html-parts/topaside.html', 'utf8'),
      footer = fs.createReadStream('./html-parts/footer.html', 'utf8');

let name = process.argv.slice(2),
    htmlray = [],
    ni = 0,
    nl = name.length;
for (ni; ni < nl; ni ++) {
    let cheadP = './html-parts/' + name[ni] + '-head.html',
        mainP = './html-parts/' + name[ni] + '-main.html',
        htmlP = name[ni] + '.html',
        chead = fs.createReadStream(cheadP, 'utf8'),
        main = fs.createReadStream(mainP, 'utf8'),
        html = fs.createWriteStream(htmlP, 'utf8');
    //let those parts form an array
    htmlray = [html, head, chead, topaside, main, footer];
    openendPipe(htmlray[1], htmlray[0]);
    htmlray[1].on('end', () => {
        openendPipe(htmlray[2], htmlray[0]);
        htmlray[2].on('end', () => {
            openendPipe(htmlray[3], htmlray[0]);
            htmlray[3].on('end', () => {
                openendPipe(htmlray[4], htmlray[0]);
                htmlray[4].on('end', () => {
                    htmlray[5].pipe(htmlray[0]);
                    htmlray[5].on('end', () => {
                        console.log(name + '.html' + ' created');
                    });
                });
            });
        });
    });
}
function openendPipe(src, dst) {
    return src.pipe(dst, {end: false});
}

但是如果 htmlray 有 100 个部分,我希望能够进行迭代以替换这些代码,我们称之为 pipeblock

openendPipe(htmlray[1], htmlray[0]);
    htmlray[1].on('end', () => {
        openendPipe(htmlray[2], htmlray[0]);
        htmlray[2].on('end', () => {
            openendPipe(htmlray[3], htmlray[0]);
            htmlray[3].on('end', () => {
                openendPipe(htmlray[4], htmlray[0]);
                htmlray[4].on('end', () => {
                    htmlray[5].pipe(htmlray[0]);
                    htmlray[5].on('end', () => {
                        console.log(name + '.html' + ' created');
                    });
                });
            });
        });
    });

我尝试了这些解决方案,但没有奏效:
解决方案一:

(function () {
    let i = 0, count = 1;
    function nextpipe() {
        let arr = arguments[0];
        i ++;
        if (count > 5) return;
        openendPipe(arr[i], arr[0]);
        count ++;
        arr[i].on('end', nextpipe);
            }
    return nextpipe;
})();
//then replace 'pipeblock' with 'nextpipe(htmlray)';
//console.log: nextpipe is undefined.

解决方案 2:

//replace 'pipeblock' with these code
let pi = 1,
    pl = htmlray.length - 1;
htmlray[pi].pipe(htmlray[0], {end: false});
htmlray[pi].on('end', nextpipe);
function nextpipe() {
    if (pi > pl) return console.log(name + '.html' + ' created');;
    pi ++;
    htmlray[pi].pipe(htmlray[0], {end: false});
    htmlray[pi].on('end', nextpipe);
}
//cosole.log: 
//htmlray[pi].pipe(htmlray[0], {end: false});
//TypeError: Cannot read property 'pipe' of undefined

【问题讨论】:

    标签: node.js asynchronous iteration asynccallback


    【解决方案1】:

    这个东西叫做“回调地狱”,你应该使用一些库来处理像async.js这样的异步调用,或者(更好)使用promises。

    只需像这样简单地承诺fs.readFile(或者为fs.createReadStream写你自己的承诺,你想使用它)

    const readFile = Promise.promisify(require('fs').readFile);
    

    然后使用Promise.all()结合您的请求承诺

    这里是 http://bluebirdjs.com/docs/api/promise.promisify.html 的示例,http://bluebirdjs.com/docs/api/promise.all.html 用于 fs 用于 Bluebird Promise 库。

    【讨论】:

    • 是的,我看过几个视频,不时阅读有关 Promise 和 generator 的文档,但我每次都忽略它们。好吧,如果它们是唯一的解决方案而不是迭代,我想我必须尝试一下。谢谢提醒。
    • 我自己在一两年前已经浪费了很多时间试图跳过这些东西(我对此感到非常遗憾),并尝试使用回调循环、异步瀑布等 - 承诺是优雅和强大的解决方案。并且已经有了错误处理(这也应该很重要) - 只需将 .catch() 添加到您的最终承诺中。
    • 等等,我只记得对于我的异步回调 100 次的问题,承诺您仍然需要在下一次调用中使用 then() 100 次。在生成器中,您仍然需要产生 100 次。我对吗?我真的不想那样做。
    • 不,你必须创建一个承诺数组,然后将其传递给Promise.all()Promise.join()(我已经打开了自己的代码,我使用了join),所以@ 987654333@
    • 谢谢,让我看看您的建议并通读文档。我真的需要彻底了解事情。这可能需要整个晚上。完成后我将发布最终解决方案代码。待会见。
    【解决方案2】:

    在阅读 async 和 Promise 文档时,我进入了关于并行、系列和瀑布的部分。我的问题是关于创建一个 html 页面,所以它不能并行完成。但是文档开始并谈论了很多关于并行的内容,它们只是让我感到困惑。掌握所有这些可能需要很长时间。无论如何,在我进行试验时,我想出了一个自己的简单解决方案。
    在我的问题中,重复部分是关于检查以前的html-part是否使用readingStream.on('end', &lt;do next writing&gt;);完成了编写,所以我把它变成了一个函数:

    function ifend(src, src2, dst) {
        return src.on('end', () => {return openendPipe(src2, dst);});
    }
    

    那我可以把pipeblock变成:

    openendPipe(htmlray[0], html);
    ifend(htmlray[0], htmlray[1], html);
    ifend(htmlray[1], htmlray[2], html);
    ifend(htmlray[2], htmlray[3], html);
    ifend(htmlray[3], htmlray[4], html);
    

    然后我可以在函数中进行迭代:

    function createHtml(src, dst) {
        let l = src.length - 1, 
            i = 0;
        openendPipe(src[0], dst);
        for (i; i < l; i ++) {
            //iterate ifend function.
            ifend(src[i], src[i + 1], dst);
        }
        return dst;
    }
    

    现在我可以用这个替换pipeblock

    createHtml(htmlray, html);
    

    【讨论】:

      猜你喜欢
      • 2018-10-09
      • 2012-06-03
      • 2020-06-04
      • 2016-07-10
      • 2017-09-09
      • 2018-08-22
      • 2017-03-20
      • 2013-02-20
      • 2015-11-03
      相关资源
      最近更新 更多