marvindanig marvindanig - 4 months ago 32
Node.js Question

For loop in promise.then()?

I need to iterate between two values and create/touch files (I/O) on each iteration.

I'm using the fs-promise module to do so asynchronously:

const path = require('path');
const fsp = require('fs-promise');


function addPages(startAt, pages, mode) {
let htmlExt = mode.HTML;
let cssExt = mode.CSS;

fsp.readFile(path.join('.', 'templates', 'body.html'), { encoding: 'utf-8' })
.then((content) => {
// return Promise.all(() => {}).then().catch(); // Do this.

for (let i = startAt, endAt = startAt + pages; i < endAt; i++) {
console.log(i);

fsp.writeFile(path.join('.', 'manuscript', `page-${i}`, `style.${cssExt}`), '')
.then(() => { console.log('Yay!') })
.catch(console.log.bind(console));

// fsp.writeFile(path.join('.', 'manuscript', `page-${i}`, `style.${cssExt}`), '')
// .then((i, templateHTML) => {
// fsp.writeFile(path.join('.', 'manuscript', `page-${i}`, `body.${htmlExt}`), content);
// })
// .catch((err) => {
// console.log.bind(console);
// });

}

})
.catch((err) => {
if (err) return error('Couldn\'t create pages', err);
});


Now I did read that
Promises.all([Array of promises])
is the way to go for looping inside the
then()
scope, but the question is why/how?

I'm unable to wrap my head around why the
for-loop
doesn't execute before the context moves out of the promised then() scope, and then how should I get to the expected outcome.

Answer
const path = require('path');
const fsp = require('fs-promise');

function addPages(startAt, pages, mode) {
    let htmlExt = mode.HTML;
    let cssExt = mode.CSS;

    return fsp.readFile(path.join('.', 'templates', 'body.html'), { encoding: 'utf-8' })
        .then((content) => {
            var pendingWrites = [];

            for (let i = startAt, endAt = startAt + pages; i < endAt; i++) {
                let filename = path.join('.', 'manuscript', `page-${i}`, `style.${cssExt}`);
                let thisWrite = fsp.writeFile(filename, '');

                pendingWrites.push(thisWrite);
            }

            return Promise.all(pendingWrites);
        })
        .catch((err) => {
            // either fully recover from the error or rethrow
            console.log("Could not add pages: ", err);
            throw err;
        });
}

As elaborated in the comments, resist the temptation to introduce none-functional .catch() handlers into your promise chain.

Non-functional means in this case: It does not recover from the error and does not rethrow the error. A catch handler that does not throw marks an error as handled, i.e. it returns a resolved promise, not a rejected one. This makes proper error handling later in the promise chain impossible. It's bad practice and unhelpful.

If you want to log the error, log it and rethrow it. If you have fully recovered from the error and subsequent code is unimpeded, don't rethrow.

Comments