0

I have done much research on this issue over internet still could not know the cause of the issue.

The issue is, when I use parallel function from async library, I am getting an error:

Callback was already called.

Here is my code: (I have commented on the line where I get this error)

var sendNotificationAddToMyRememberNew = function(request, response, restaurant) {

    async.parallel({
        notification_list: function (callback) {
            models.NotificationList.create({
                alert_msg: "Restaurant " + restaurant.name + " remembered.",
                payload: JSON.stringify({
                    restaurant_id: restaurant.id,
                    restaurant_name: restaurant.name,
                    restaurant_image: restaurant.image
                }),
                type: constants.NOTIFICATION_TYPE.RESTAURANT_REMEMBERED,
                platform_type: constants.MOBILE_PLATFORM_TYPE.ALL,
                status: constants.NOTIFICATION_STATUS.SENT,
                device_token: null,
                device_arn: null,
                schedule_time: moment.utc(),
                customer_id: request.token.customer_id,
                customer_email: request.token.email,
                created_date: moment.utc(),
                updated_date: moment.utc()
            }).then(function(notificationList){
                callback(null, notificationList);
            }).catch(function(error){
                callback(error);
            });
        },
        badge_count: function(callback) {

            models.CustomerBadgeCount.findOrCreate({
                where: {
                    customer_id: request.token.customer_id
                },
                defaults: {
                    badge_count: 1,
                    customer_id: request.token.customer_id,
                    created_date: moment.utc(),
                    updated_date: moment.utc()
                }
            }).spread(function (badgeCount, created) {
                if(!created){
                    badgeCount.update(
                        {
                            badge_count: badgeCount.badge_count + 1,
                            updated_date: moment.utc()
                        },
                        {
                            fields:[
                                "badge_count",
                                "updated_date"
                            ]
                        }
                    ).then(function(badgeCount) {
                        callback(null, badgeCount);
                    }).catch(function (error) {
                        callback(error); // Getting error of callback here
                    });
                }else{
                    callback(null, badgeCount)
                }
            }).catch(function (error) {
                callback(error);
            });

        },
        devices: function(callback) {
            models.CustomerDeviceTokens.findAll({
                where: {
                    customer_email: request.token.email
                }
            }).then(function(devices) {
                callback(null, devices);
            }).catch(function(error) {
                callback(error);
            });
        }
    }, function(error, results){

        if(error) {
            Logger.logDbError(error,request);
            return console.log(error);
        }
        //callback function
        console.log("-------------------------------------------------------");
        console.log("Notification List: " + JSON.stringify(results.notification_list, null, 4));
        console.log("badge_count: " + JSON.stringify(results.badge_count, null, 4));

        if(results.devices && result.devices.count > 0) {
            results.devices.forEach(function(device) {
                console.log("device_arn: " + device.device_arn);
            }, this);
        }
    });

}
Vishal
  • 6,238
  • 10
  • 82
  • 158
  • Don't use async.js with promises. Plain `Promise.all` is so much easier. – Bergi Jul 13 '17 at 04:20
  • @Bergi This question is not duplicate. I have solved this question. Please take a look at my answer. – Vishal Jul 13 '17 at 04:23
  • The duplicate explains the [difference between `.then(…, …)` and `.then(…).catch(…)`](https://stackoverflow.com/q/24662289/1048572) which is the cause for getting `callback` called twice. – Bergi Jul 13 '17 at 04:26
  • if you feel that this question is duplicate then please delete it. Beacause I cannot delete it as this question already has answers. – Vishal Jul 13 '17 at 04:50
  • @Bergi sorry, I forgot to mention your name in above comment. Please read it. – Vishal Jul 13 '17 at 05:30
  • I can't delete it either. – Bergi Jul 13 '17 at 06:16

2 Answers2

1

This can happen if callback(null, badgeCount) throws an error (which could happen if the final callback throws an error for some reason).

What happens in that case is that the .catch handler is called, in which you call callback again, resulting in the "already called" message.

To prevent this (or rather, to catch these errors), you shouldn't use .catch but use the two-argument version of .then:

.then(function(badgeCount) {
  callback(null, badgeCount);
}, function (error) {
  callback(error);
});

(and similar for all other locations in your code where you use .then().catch() inside an async operation)

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Good answer!!!!!! But I have figured it out. I will post another one. Thanks for the answer and your time. – Vishal Jul 13 '17 at 04:10
0

I used findOrCreate method of sequelize in my code. The original syntax of findOrCreate is :

model.SomeModelName.findOrCreate({
    ....
    ....
}).spread(function(data, created) {

}).fail(function(err) {

});

As you can see in above code that .spread() should be followed by .fail(), But in my code, I used .spread() followed by .catch(). So, both the blocks were executed and thus callback was called twice

Vishal
  • 6,238
  • 10
  • 82
  • 158
  • That might be how you get the exception inside the `then`, but it's not the actual issue. – Bergi Jul 13 '17 at 04:27