0

I'm working on adding notifications to my chrome extension that lets users chat with one another. Each user has a chats section that lists all the chatrooms they're in, along with the last message they saw. To check if a message was sent while they were away, I'm trying to loop through the chatrooms they're in and seeing if the last message sent was added to the database after the last message they saw was added.

Here's my code, I'm using a noSQL database called firebase:

var checkNotifications = function(user){
   var notification = false;
   firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
      for (var chat in snapshot.val()){
         var lastMessage = snapshot.val()[chat];
         firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
            if (Object.keys(snap.val())[0] > lastMessage){
               notification = true;    // Message is newer than their last seen message
            }
         });
      }
   });
}

I'm running into the issue where before the second database call to return the messages from the chatroom currently being looked at in the for loop the for loop already moves on and updates the variable lastMessage, so that variable ends up holding the last message from a chatroom further down the chain.

Is there a way to make this less asynchronous so that the variable lastMessage has the same value for each step in the for loop?

Community
  • 1
  • 1
MarksCode
  • 8,074
  • 15
  • 64
  • 133
  • What is the desired process here? Do you want to iterate serially and stop when you find a match? Do you want iterate in parallel and know if any operation meets your condition? – jfriend00 Aug 03 '16 at 04:10
  • My end goal is to get an array where each index has either `true` or `false` depending on whether a message was sent after the last message seen by the user, but for now I just want to know if any message was sent after the last seen message seen in the corresponding chatroom. – MarksCode Aug 03 '16 at 04:21

2 Answers2

1

To check if a message was sent while they were away, I'm trying to loop through the chatrooms they're in and seeing if the last message sent was added to the database after the last message they saw was added.

transactions take a function and are the proper tool for this.

When working with data that could be corrupted by concurrent modifications, such as incremental counters, you can use a transaction operation. You can give this operation an update function and an optional completion callback. The update function takes the current state of the data as an argument and returns the new desired state you would like to write. If another client writes to the location before your new value is successfully written, your update function is called again with the new current value, and the write is retried. For instance, in the example social blogging app, you could allow users to star and unstar posts and keep track of how many stars a post has received as follows:

function toggleStar(postRef, uid) {
  postRef.transaction(function(post) {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
1

This should work. It captures the variable in a closure: (function(){ ... })();

var checkNotifications = function(user){
    var notification = false;
    firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
        for (var chat in snapshot.val()){
            (function(){
                var lastMessage = snapshot.val()[chat];
                firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
                    if (Object.keys(snap.val())[0] > lastMessage){
                        notification = true;    // Message is newer than their last seen message
                    }
                });
            })();
        }
    });
}

Or this:

var checkNotifications = function(user){
    var notification = false;
    firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
        var func = function(lastMessage) {
            firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
                if (Object.keys(snap.val())[0] > lastMessage){
                    notification = true;    // Message is newer than their last seen message
                }
            });
        };
        for (var chat in snapshot.val()){
            func(snapshot.val()[chat]);
        }
    });
}
Stephen S.
  • 33
  • 5
  • But, this has no reliable place to evaluate the `notification` flag because the operations are async. You can't just check that flag at the end of the `for` loop. The async operations have not yet finished. This will not work properly. – jfriend00 Aug 03 '16 at 04:12
  • You are right. I missed that part. The `notification` flag has no effect in the code. One solution might be to pass in a callback function to checkNotifications that gets called after the last iteration of func(), which can be determined via a counter variable. – Stephen S. Aug 03 '16 at 04:44