0

What I am doing is using fs to stitch 5 pieces of html-page parts(html, head, chead, topaside, main, footer) together. The file name is htmlpage.js, so you can just run node htmlpage.js file1 file2 file3 ... in command line tool, and it will stitch those html-page parts together, then spit out file1.html, file2.html, file3.html .... I don't like to use template engine/library/framework or whatever, especially when I am learning.
Here is the sorce code:

'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});
}

But what if the htmlray has 100 parts, I want to be able to do an iteration to replace these code, let's call it 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');
                    });
                });
            });
        });
    });

I tried these solutions, they didn't work:
Solution 1:

(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.

Solution 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
alexcres
  • 53
  • 2
  • 12

2 Answers2

1

This thing calls "callback hell" and you should use either some library to handle async calls like async.js or (better) use promises.

Just simply promisify fs.readFile like this (or write your own promise for fs.createReadStream it you want to use it)

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

and then combine your request promises using Promise.all()

Here are examples http://bluebirdjs.com/docs/api/promise.promisify.html, http://bluebirdjs.com/docs/api/promise.all.html for fs for Bluebird promise library.

Andrea Korinski
  • 275
  • 1
  • 8
  • Yeah, I have watched couple of videos and read the documentation about promise and generator from time to time, but I dismissed them every time. Well, if they are the only solution but not iteration, I guess I have to try them out. Thanks for the reminder. – alexcres May 31 '16 at 15:38
  • I've lost plenty of time myself year or two ago trying to skip this stuff (and I really regret about this), and trying to experiment with callback loops, async waterfalls etc - promises are elegant and powerful solution. And already with error handling (this should be also important) - just add `.catch()` to you final promise. – Andrea Korinski May 31 '16 at 15:44
  • Waite, I just remember that for my question of async callback 100 times, in promise, you still need to use then() for the next call 100 times. And in generator you still need to yield 100 times. Am I right? I don't really want to do that. – alexcres May 31 '16 at 15:46
  • No, you have to create an array of the promises and then pass it to the `Promise.all()` or `Promise.join()` (I've opened my own code, I used `join`), so `Promise.join(...yourPromisesArray, (...arrayOfReplies) => { processReplies(arrayOfReplies) })` – Andrea Korinski May 31 '16 at 15:51
  • 1
    Thanks, let me walk through your suggestion and read through documentation. I really need to understand things thoroughly. This is probably going to take the whole night. I will post final solution code when its done. See you later. – alexcres May 31 '16 at 15:58
0

While I am reading through async and promise documentation, I get to the part about parallel, series and waterfall. My problem is about creating an html page, so it can't be done in parallel. But the documentation starts and talks a lot about parallel, and they just add confusion to my head. To grasp all of them is probably going to take a long time. Anyway, while I am experimenting, I come up with an easy solution of my own.
In my problem, the repetitive part is about check if previous html-part has done writing by using readingStream.on('end', <do next writing>);, so I make it into a function:

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

Then I can turn pipeblock into:

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);

Then I can do an iteration in a function:

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;
}

Now I can replace pipeblock with this:

createHtml(htmlray, html);
alexcres
  • 53
  • 2
  • 12