Sean
Sean

Reputation: 1364

Promise in Node not working - .then is undefined

I have this really weird issue where my Promise's .then is not working. The error is below:

TypeError: Cannot read property 'then' of undefined

The reason I call this weird is because I am using the same structure everywhere else and it works flawlessly. I would also like to add that the actual action it is supposed to do - does get done! So this is purely an issue with the way my Promises are "coded" - just syntactic sugar.

My code snippet is as below:

var updateAllUnreadNotifications = function(){
    userModel.aggregate([
        {$match:{"profileID": 123}},
        {$unwind: "$notifications"},
        {$match:{"notifications.read": false}},
        {$group: {_id:"$_id", notifications: {$push:"$notifications"}}}
        ], function(err, notifications){
             var notificationPromises = notifications.map(function(notification) {
                return new Promise(function(resolve){
                    userModel.findOneAndUpdate(
                        {profileID: 123, "notifications.id": notification.id},
                        {"notifications.$.read": true},
                        {safe: true},
                        function(err, result) {
                            if (err){
                               return;
                            } else if (result){
                                resolve(notification);
                            }
                        }
                    );
                });
            });

            return Promise.all(notificationPromises);
        });
};

updateAllUnreadNotifications()
.then(function(notifications){
    res.json({
        message: 'All is well'
    });
});

Furthermore, I have done a console.log on the notificationPromises and it does look like a chain of Promises. And of course, the result variable is also there so this is not an issue with the DB command.

What have I messed up? I am sure it will be a very obvious one but for the life of me, I can't isolate it.

Let me know if some more information is required.

Upvotes: 0

Views: 237

Answers (2)

Bergi
Bergi

Reputation: 665364

You return from the userModel.aggregate callback - which cannot work - instead of from the updateAllUnreadNotifications function. It does return undefined, on which you try to invoke the then method. You will need to promisify that as well:

function aggregateAsync(x) {
    return new Promise((resolve, reject) => {
        userModel.aggregate(x, (err, res) => {
            if (err) reject(err);
            else resolve(res);
        });
    });
}
function findOneAndUpdateAsync(x, y, z) {
    return new Promise((resolve, reject) => {
        userModel.findOneAndUpdate(x, y, z, (err, res) => {
            if (err) reject(err);
            else resolve(res);
        });
    });
}

With those, you can do

function updateAllUnreadNotifications() {
    return aggregateAsync([
        {$match:{"profileID": 123}},
        {$unwind: "$notifications"},
        {$match:{"notifications.read": false}},
        {$group: {_id:"$_id", notifications: {$push:"$notifications"}}}
    ]).then(notifications => {
        var notificationPromises = notifications.map(notification => {
            return findOneAndUpdateAsync(
                {profileID: 123, "notifications.id": notification.id},
                {"notifications.$.read": true},
                {safe: true}
            );
        });
        return Promise.all(notificationPromises);
    });
}

updateAllUnreadNotifications().then(notifications => {
    res.json({
       message: 'All is well'
    });
});

Upvotes: 1

Prakash Sharma
Prakash Sharma

Reputation: 16482

You need to return promise from updateAllUnreadNotifications function but you are not returning anything from that function. That's why you are getting that error.

I would say a better way is that make a separate function for processing notification and chain the promises like this

    var updateAllUnreadNotifications = function(){
      return new Promise(function(resolve,reject){
        userModel.aggregate([
            {$match:{"profileID": 123}},
            {$unwind: "$notifications"},
            {$match:{"notifications.read": false}},
            {$group: {_id:"$_id", notifications: {$push:"$notifications"}}}
            ], function(err, notifications){
                 if(err){ reject(err)}
                 // If got notifications then resolve
                 // notifications will be available to next then function
                 resolve(notifications)
             })
         })
      }

      var processNotifications = function(notifications) {
         var notificationPromises = notifications.map(function(notification) {
              return new Promise(function(resolve){
                 userModel.findOneAndUpdate(
                    {profileID: 123, "notifications.id": notification.id},
                     {"notifications.$.read": true},
                            {safe: true},
                            function(err, result) {
                                if (err){
                                   return;
                                } else if (result){
                                    resolve(notification);
                                }
                            }
                        );
                    });
                });

         return Promise.all(notificationPromises);
       };

    // Chain one extra then here to process notifications
    updateAllUnreadNotifications().then(processNotifications)
    .then(function(notifications){
        res.json({
            message: 'All is well'
        });
    });

Upvotes: 1

Related Questions