Mahesh
Mahesh

Reputation: 190

async waterfall callback was already called error in nodejs

I am newbee to nodejs. I have been scratching my head for long time on why below code throwing error.I found some similar in stack overflow, but couldn't get help in finding the bug.

var albums_coll,photos_coll;    
    async.waterfall([
      function(cb){
        MongoClient.connect(url,
         (err,dbase)=>{
          if(err) {
            console.log('bad');
            process.exit(-1);
          }
          console.log("I have a connection!");
          db=dbase;
          cb(null);
        }
        );
      },

      function(cb){
        db.collection("albums",cb);
      },

      function(album_obj,cb){
        albums_coll = album_obj;
      db.collection("photos",cb);
    },

    function(photos_obj,cb){
      photos_coll = photos_obj;
      cb(null);

    },

    function(cb){
      albums_coll.InsertOne(a1,cb);
    },
    function(inserted_doc,cb){
      console.log("I have Inserted a document!!");
      console.log(inserted_doc);
      cb(null);
    }
    ],
    function(err,results){
      console.log("Done!!!");
      console.log(err);
      console.log(results);
      db.close();
    });

Please suggest!!

the following is the error its showing.

I have a connection!
C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\mongodb\lib\mongo_client.js:433
          throw err
          ^

Error: Callback was already called.
    at C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\async\dist\async.js:903:32
    at Db.collection (C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\mongodb\lib\db.js:466:27)
      .
      .           
      .

Upvotes: 0

Views: 1270

Answers (1)

M3talM0nk3y
M3talM0nk3y

Reputation: 1432

The callback() function you are passing into the db.collection() function is being called twice. The first time this happens is inside the MongoDB library while invoking db.collection(). It is happening in the following block - I added a comment pointing to line 466 as shown in the stack:

if(options == null || !options.strict) {
    try {
      var collection = new Collection(this, this.s.topology, this.s.databaseName, name, this.s.pkFactory, options);
      if(callback) callback(null, collection);
      return collection;
    } catch(err) {
      // if(err instanceof MongoError && callback) return callback(err);
      if(callback) return callback(err);  // <-- Line 466 - first time callback is called
      throw err;
    }
  }

The second time it gets called, is by the async library when it needs to determine if it has to execute the next task in the array or invoke the final callback; by then, the callback is already null. The error you're seeing is being raised in the onlyOnce() function that is part of the async library:

function onlyOnce(fn) {
    return function() {
        if (fn === null) throw new Error("Callback was already called.");
        var callFn = fn;
        fn = null;
        callFn.apply(this, arguments);
    };
}

So the following code will raise the error:

async.waterfall([
    function(callback) {
        MongoClient.connect('mongodb://localhost:27017/test', (error, db) => {
            if (error) {
                console.error(error);
                process.exit(-1);
            }
            callback(null, db);
        });
    },
    function(db, callback) {
        console.log('Querying albums collection...');

        // This will cause the exception
        db.collection('albums', callback);
    },
    function(db, albums, callback) {
        console.log('Got albums...');
        console.log('Querying photos collection...');

        db.collection('photos', (error, photos) => {
            if (error) {
                return callback(error);
            }

            callback(null, db, albums, photos);
        });
    },
    function(db, albums, photos, callback) {
        console.log('Got photos...');

        callback(null, 'DONE');
    }
], function (error, results) {
    console.error(error);
    console.log(results);

    process.exit(0);
});

While this version of the code will execute as expected:

async.waterfall([
    function(callback) {
        MongoClient.connect('mongodb://localhost:27017/test', (error, db) => {
            if (error) {
                console.error(error);
                process.exit(-1);
            }
            callback(null, db);
        });
    },
    function(db, callback) {
        console.log('Querying albums collection...');

        db.collection('albums', (error, albums) => {
            if (error) {
                return callback(error);
            }

            callback(null, db, albums);
        });
    },
    function(db, albums, callback) {
        console.log('Got albums...');
        console.log('Querying photos collection...');

        db.collection('photos', (error, photos) => {
            if (error) {
                return callback(error);
            }

            callback(null, db, albums, photos);
        });
    },
    function(db, albums, photos, callback) {
        console.log('Got photos...');

        callback(null, 'DONE');
    }
], function (error, results) {
    console.error(error);
    console.log(results);

    process.exit(0);
});
/* Output:
Querying albums collection...
Got albums...
Querying photos collection...
Got photos...
null
DONE
*/

Hope this helps!

Upvotes: 0

Related Questions