关于javascript:已经调用了回调。 但在此之前我从未打过电话回叫。 async.parallel中的一个错误?

Callback was already called. But I never call callback before that. A bug in async.parallel?

本问题已经有最佳答案,请猛点这里访问。

我已经在互联网上对这个问题做了很多研究,但仍然无法知道问题的原因。

问题是,当我从异步库使用并行函数时,我收到一个错误:

1
Callback was already called.

这是我的代码:(我已经评论了我收到此错误的行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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);
        }
    });

}


如果callback(null, badgeCount)引发错误(如果最终回调因某种原因引发错误,则可能发生这种情况),就会发生这种情况。

在这种情况下会发生的是调用.catch处理程序,在其中再次调用callback,从而产生"已调用"消息。

为了防止这种情况(或者更确切地说,捕获这些错误),您不应该使用.catch,而是使用.then的双参数版本:

1
2
3
4
5
.then(function(badgeCount) {
  callback(null, badgeCount);
}, function (error) {
  callback(error);
});

(对于在async操作中使用.then().catch()的代码中的所有其他位置,类似)


我在我的代码中使用了findOrCreate sequelize方法。 findOrCreate的原始语法是:

1
2
3
4
5
6
7
8
model.SomeModelName.findOrCreate({
    ....
    ....
}).spread(function(data, created) {

}).fail(function(err) {

});

正如您在上面的代码中看到的那样,.spread()后面应该跟.fail(),但在我的代码中,我使用.spread()后跟.catch()
因此,两个块都被执行,因此回调被调用了两次