Search code examples
node.jsasync.jswaterfall

NodeJs async waterfall (callback method is not a function)


I am having a problem using async waterfall where I find that after calling the second callback (cbNumPages), the first parameter "pages" is the actual callback for the next function, instead of the last parameter "cbGetFiles" which it should be (as far as I know async waterfall says that last parameter should always be the callback, well in this case is apparently not).

The code is the following:

async.waterfall
            ([
                function(cbNumPages)
                {
                    request({
                        url: 'any-url',
                        qs: {},
                        method: 'GET',
                        headers: {
                            'Authorization' : 'any-auth'
                        }
                    }, (err, response, body) => {
                        if (!err && response.statusCode == 200)
                        {
                            var $ = cheerio.load(body);
                            var pagesList = $('ol.aui-nav').children();
                            if(pagesList.length<1)
                            {
                                var numPages = 1;
                            } else {
                                var numPages = pagesList.length-2;
                            }
                            console.log(numPages);
                            var pages = new Array(numPages),
                                total = numPages*20,
                                iterator = 0;

                            async.eachSeries(pages, function(page, cb)
                            {
                                if(page>1)
                                {
                                    pages[iterator] = iterator;
                                }else {
                                    pages[iterator] = iterator*20;
                                }
                                iterator++;
                                cb();
                            }, function(err){
                                if(err) cbNumPages(err);
                                cbNumPages(null, pages);
                            });
                        } else {
                            cbNumPages(err);
                        }
                    })
                },

                function(pages, cbGetFiles)
                {
                    var files = [];
                    var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();

                    async.eachSeries(pages, function(page, cb)
                    {
                        request({
                            url: 'any-url'+page,
                            qs: {},
                            method: 'GET',
                            headers: {
                                'Authorization' : 'any-auth'
                            }
                        }, (err, response, body) => {
                            if(!err && response.statusCode == 200)
                            {
                                var $ = cheerio.load(body);
                                var rows = $('tr[id^=\'attachment-\']');
                                async.eachLimit(rows, 1, function(row, cb)
                                {
                                    var id = row.attribs['id'];
                                    var file = row.attribs['data-attachment-filename'];
                                    var author = $(row).children('.creator').text().trim();
                                    var created = $(row).children('.created-date').text().trim();
                                        created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
                                    var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
                                    var extension = row.attribs['data-attachment-filename'].split('.');
                                        extension = extension[extension.length-1];
                                    if(created<limitDate && validExtensions.indexOf(extension)>-1)
                                    {
                                        var f = '{ "id": "' + id + '",';
                                            f += ' "file": "' + file + '",';
                                            f += ' "url": "' + urlFile + '",';
                                            f += ' "author": "' + author + '",';
                                            f += ' "modified": "' + created + '" }';
                                        files.push(JSON.parse(f));
                                    }
                                    cb();
                                }, (err) => {
                                    if(err) cbGetFiles(err);
                                });
                                cb();
                            } else {
                                cb(err);
                            }
                        });
                    }, function(err){
                        if(err){
                            cbGetFiles(err);
                        } else {
                            cbGetFiles(null, files);
                        }
                    });
                },

                function(files, cbGetAutors)
                {
                    var filesFinal = {};
                    for(var f in files)
                    {
                        if(!filesFinal[files[f].author])
                        {
                            var ff = {};
                            for(var i in files)
                            {
                                if(files[i].author === files[f].author)
                                {
                                    ff[files[i].file] = files[i].url;
                                }
                            }
                            filesFinal[files[f].author] = ff;
                        }
                    }
                    cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
                },

                function(filesFinal, cbSendEmail)
                {
                    var authors = Object.keys(filesFinal);
                    async.eachSeries(authors, function(author, cb)
                    {
                        var name = author.split(' ');

                        var email = 'simple-mail@gmail.com';
                        var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
                        for(var a in Object.keys(filesFinal[author]))
                        {
                            msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
                            msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
                        }
                        msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';

                        var message = {
                            text:    msg,
                            from:    'test@mail.com',
                            to:      email,
                            bcc:     '',
                            subject: 'Sample subject',
                            attachment: [{data: msg, alternative: true}]
                        };

                        serverEmail.send(message, function(err, message)
                        {
                            if(err)
                            {
                                cb(err);
                            } else {
                                console.log(message);
                                cb();
                            }
                        });

                    }, function(err){
                        if(err) cbSendEmail(err);
                        cbSendEmail();
                    });
                }

            ], (err) => {
                if(err) console.log(err);
            });

I would like to know if there is a way to control this issue or at least if there are another options for what I want to do.

Thanks.


Solution

  • A better (neat) way to use async waterfall.

    Make sure you use return before any callback function. I have added them in the code.

    Also, if you are nesting eachSeries, it is better to give a different name to callback function than the parent callback function.

    I have changed 'cb' of child async.series to 'inner_cb'

    Updated Code:

    async.waterfall
    ([
        funcOne,
        funcTwo,
        funcThree,
        funcFour    
    ], (err) => {
        if(err) console.log(err);
    });
    
    funciton funcOne(cbNumPages) {
        request({
            url: 'any-url',
            qs: {},
            method: 'GET',
            headers: {
                'Authorization' : 'any-auth'
            }
        }, (err, response, body) => {
            if (!err && response.statusCode == 200)
            {
                var $ = cheerio.load(body);
                var pagesList = $('ol.aui-nav').children();
                if(pagesList.length<1)
                {
                    var numPages = 1;
                } else {
                    var numPages = pagesList.length-2;
                }
                console.log(numPages);
                var pages = new Array(numPages),
                    total = numPages*20,
                    iterator = 0;
    
                async.eachSeries(pages, function(page, cb)
                {
                    if(page>1)
                    {
                        pages[iterator] = iterator;
                    }else {
                        pages[iterator] = iterator*20;
                    }
                    iterator++;
                    return cb();
                }, function(err){
                    if(err) return cbNumPages(err);
                    return cbNumPages(null, pages);
                });
            } else {
                return cbNumPages(err);
            }
        })
    }
    
    function funcTwo(pages, cbGetFiles) {
        var files = [];
        var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
    
        async.eachSeries(pages, function(page, cb)
        {
            request({
                url: 'any-url'+page,
                qs: {},
                method: 'GET',
                headers: {
                    'Authorization' : 'any-auth'
                }
            }, (err, response, body) => {
                if(!err && response.statusCode == 200)
                {
                    var $ = cheerio.load(body);
                    var rows = $('tr[id^=\'attachment-\']');
                    async.eachLimit(rows, 1, function(row, inner_cb)
                    {
                        var id = row.attribs['id'];
                        var file = row.attribs['data-attachment-filename'];
                        var author = $(row).children('.creator').text().trim();
                        var created = $(row).children('.created-date').text().trim();
                            created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
                        var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
                        var extension = row.attribs['data-attachment-filename'].split('.');
                            extension = extension[extension.length-1];
                        if(created<limitDate && validExtensions.indexOf(extension)>-1)
                        {
                            var f = '{ "id": "' + id + '",';
                                f += ' "file": "' + file + '",';
                                f += ' "url": "' + urlFile + '",';
                                f += ' "author": "' + author + '",';
                                f += ' "modified": "' + created + '" }';
                            files.push(JSON.parse(f));
                        }
                        return inner_cb();
                    }, (err) => {
                        if(err) return cbGetFiles(err);
                    });
                    return cb();
                } else {
                    return cb(err);
                }
            });
        }, function(err){
            if(err){
                return cbGetFiles(err);
            } else {
                return cbGetFiles(null, files);
            }
        });
    }
    
    function funcThree(files, cbGetAutors) {
        var filesFinal = {};
        for(var f in files)
        {
            if(!filesFinal[files[f].author])
            {
                var ff = {};
                for(var i in files)
                {
                    if(files[i].author === files[f].author)
                    {
                        ff[files[i].file] = files[i].url;
                    }
                }
                filesFinal[files[f].author] = ff;
            }
        }
        return cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
    }
    
    function funcFour(filesFinal, cbSendEmail) {
        var authors = Object.keys(filesFinal);
        async.eachSeries(authors, function(author, cb)
        {
            var name = author.split(' ');
    
            var email = 'simple-mail@gmail.com';
            var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
            for(var a in Object.keys(filesFinal[author]))
            {
                msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
                msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
            }
            msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
    
            var message = {
                text:    msg,
                from:    'test@mail.com',
                to:      email,
                bcc:     '',
                subject: 'Sample subject',
                attachment: [{data: msg, alternative: true}]
            };
    
            serverEmail.send(message, function(err, message)
            {
                if(err)
                {
                    return cb(err);
                } else {
                    console.log(message);
                    return cb();
                }
            });
    
        }, function(err){
            if(err) return cbSendEmail(err);
            return cbSendEmail();
        });
    }