1
\$\begingroup\$

In my script, node.js reads text files and generates text itself. At the end, I have five "text parts", and want to add them to one file. For this step, it must be considered that the sequence is important, so unfortunately the great async technique of node is obstructive. How would you handle this?

The five text parts consists of:

  1. Read File "head.txt" and than write/append its text (not much data) to the file
  2. Text (not much data) generated by node itself -> write/append its text to the file
  3. Text (large data) generated by node itself (loop)
  4. Text (large data) generated by node itself (loop)
  5. Read File "footer.txt" and than write/append its text (normal size) to the file

var fs = require('fs');

var filename = "content.txt";

// (1) Read 'head.txt'
fs.readFileSync('head.txt', 'utf8', function (err, head) {
    if (err) {
        return console.log('Could not read file.');
    }
    fs.appendFile(filename, head);
    //var writeStream = fs.createWriteStream(filename);
    //writeStream.write(head);
});

// (2)
var a = '...';
var b = '...';
var ab = a + b;
//var writeStream = fs.createWriteStream(filename);
//writeStream.write(ab);
//writeStream.end();
fs.appendFileSync(filename, ab);


// (3)
var writeStream = fs.createWriteStream(filename);
for (i = 0; i < xzy.length; i++) {
    writeStream.write('...' + xyz[i] + '...');
}
//writeStream.end();


// (4)
var writeStream = fs.createWriteStream(filename);
for (i = 0; i < xyz.length; i++) {
    writeStream.write('...' + xyz[i] + '...');
}
//writeStream.end();


// (5) Read 'footer.txt'
fs.readFileSync('footer.txt', 'utf8', function (err, footer) {
if (err) {
    return console.log('Could not read file.');
}
var writeStream = fs.createWriteStream(filename);
writeStream.write(footer);
//writeStream.end();
});

/*writeStream.on('finish', function () {
    console.log('finish');
});
writeStream.on('close', function () {
    console.log('close');
});*/
\$\endgroup\$
1
\$\begingroup\$

Here we go, I would create some "util" namespace to write some helper code:

./util/index.js

const bPromise = require('bluebird');

const fs = require('fs');

const readFile = bPromise.promisify(fs.readFile);

const appendFile = bPromise.promisify(fs.appendFile);

const createWS = filename => {
    var writer = fs.createWriteStream(filename, {defaultEncoding: 'utf8', flags : 'a'});
    return {
        write : bPromise.promisify(writer.write.bind(writer))
    }
}

module.exports = {
    readFile, 
    appendFile,
    createWS
}

Thanks to bluebird we can convert functions with callback functions into functions that returns a resolved or rejected Promise with the result value. So that is what it's done in "./util/index.js".

In your main file:

var { readFile, appendFile, createWS} = require('./util');

var filename = "content.txt";
var writer = createWS(filename);
var xyz = ['x', 'y', 'z'];

// (1) Read 'head.txt'
readFile('head.txt', 'utf8')
.then( head => appendFile(filename, head))
.then( ()=>{
    // Head appended
    var a = '...';
    var b = '...';
    var ab = a + b;
    return appendFile(filename, ab);
})
.then( ()=>{
    // ab appended
    //return xyz.reduce((a, b)=> a.then( ()=> writer.write('...' + b + '...') ), Promise.resolve(null) )
    // or just 
    return writer.write(xyz.map(el=>'...' + el + '...').join(''));
})
.then( ()=>{
    return writer.write(xyz.map(el=>'...' + el + '...').join(''));
})
.then( ()=>{
    return readFile('footer.txt', 'utf8')
        .then( footer => writer.write(footer))
})
.then( ()=>{})
.catch(console.error.bind(console));

Now looks very nice and it's clear. There's no need (Like in your code) to execute synchronous functions.

Check the ES5 code.

./util/index.js

var bPromise = require('bluebird');

var fs = require('fs');

var readFile = bPromise.promisify(fs.readFile);

var appendFile = bPromise.promisify(fs.appendFile);

var createWS = function createWS(filename) {
    var writer = fs.createWriteStream(filename, { defaultEncoding: 'utf8', flags: 'a' });
    return {
        write: bPromise.promisify(writer.write.bind(writer))
    };
};

module.exports = {
    readFile: readFile,
    appendFile: appendFile,
    createWS: createWS
};

index.js

var _require = require('./util');

var readFile = _require.readFile;
var appendFile = _require.appendFile;
var createWS = _require.createWS;


var filename = "content.txt";
var writer = createWS(filename);
var xyz = ['x', 'y', 'z'];

// (1) Read 'head.txt'
readFile('head.txt', 'utf8').then(function (head) {
    return appendFile(filename, head);
}).then(function () {
    // Head appended
    var a = '...';
    var b = '...';
    var ab = a + b;
    return appendFile(filename, ab);
}).then(function () {
    // ab appended
    //return xyz.reduce((a, b)=> a.then( ()=> writer.write('...' + b + '...') ), Promise.resolve(null) )
    // or just 
    return writer.write(xyz.map(function (el) {
        return '...' + el + '...';
    }).join(''));
}).then(function () {
    return writer.write(xyz.map(function (el) {
        return '...' + el + '...';
    }).join(''));
}).then(function () {
    return readFile('footer.txt', 'utf8').then(function (footer) {
        return writer.write(footer);
    });
}).then(function () {}).catch(console.error.bind(console));
\$\endgroup\$
2
  • \$\begingroup\$ Works! Just get this: var { readFile, appendFile, createWS } = require('./util'); SyntaxError: Unexpected token { and when I "splitted" each variable I get createWS is not a function \$\endgroup\$ – Maximilian Fuchs Aug 19 '16 at 21:32
  • \$\begingroup\$ It's because you have an old version of node, the latest versions comes with new ES6 features like the destructuring declaration in the code. \$\endgroup\$ – Jose Hermosilla Rodrigo Aug 20 '16 at 0:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.