Search code examples
javascriptnode.jsasynchronouspromisepg-promise

Error : Callback was already called when using pg-promise with async series


I'm having trouble understanding the output printed why executing this code :

1 2 Unhandled rejection Error: Callback was already called.

It seems like both then and catch are executed when the query is successful.

Any idea ?

Cheers

    async.series([
            function(callback) {
                db.none(query)
                    .then(function () {
                        return callback(null, true);
                    })
                    .catch(function (err) {
                        return callback(err, null);
                    });
            },
            function(callback) {
                db.any(query)
                    .then(function (data) {
                        console.log('1')
                        return callback(null, data);
                    })
                    .catch(function (err) {
                        console.log('2')
                        console.log(err);
                        return callback(err, null);
                    });
            }
        ],
        function(err, results) {
           if (results && !results[1].isEmpty()) {
              // do something
           }
        });

EDIT :

TypeError: results[1].isEmpty is not a function

It seems like the problem come from the rest of the code and was just a simple undefined function error, thanks.

But i still don't understand something : why is this error catched inside the second query instead of outside the async queries ?


Solution

  • I'm the author of pg-promise.

    You should never use async library with pg-promise, it goes against the concept of shared/reusable connections.

    Implementation with proper use of the same connection, via a task:

    db.task(t => {
        return t.batch([
            t.none(query1),
            t.any(query2)
        ]);
    })
        .then(data => {
            // data[0] = null - result of the first query
            // data[1] = [rows...] - result of the second query
    
            callback(null, data); // this will work, but ill-advised
        })
        .catch(error => {
            callback(error, null); // this will work, but ill-advised
        });
    

    See also: Chaining Queries.

    However, in your case it looks like when you call the successful callback(null, data), it throws an error, which in turn results in it being caught in the following .catch section. To test this, you can change your promise handler like this:

        .then(data => {
            callback(null, data);
        }, error => {
            callback(error, null);
        });
    

    It should normally throw an error about Promise missing .catch because you threw an error while in .then and there is no corresponding .catch chained below, which you can also check through this code:

        .then(data => {
            callback(null, data);
        }, error => {
            callback(error, null);
        })
        .catch(error => {
            // if we are here, it means our callback(null, data) threw an error
        });
    

    P.S. You really should learn to use promises properly, and avoid any callbacks altogether. I only provided an example consistent with your own, but in general, converting promises into callbacks is a very bad coding technique.