Question: Node.js + Angular.js promises sync issue

Question

Node.js + Angular.js promises sync issue

Answers 2
Added at 2016-12-31 16:12
Tags
Question

I am working on a chat app which is Node.js + MongoDB (Mongoose library) on the server side, and Angular.js on the client side.

I have a database collection (MongoDB) for rooms (all the rooms in the app), which looks like this:

// ------- creating active_rooms model -------
var active_rooms_schema = mongoose.Schema({
    room_name: String,
    users: [String]
});
var active_rooms = mongoose.model('active_rooms', active_rooms_schema);

This database contains a room with all its users (i.e. "my cool room" with users: "mike", "joe", and "dave").

What I want to do is - every time a user wants to be in a chat room (with some room name) in my Angular.js client, I want to:

  1. Create the room if it is not exists
  2. Push that user into the users array of the room.

I know that because of 1, I will always have a room with an array of users.

This is my Angular relevant code: (I cannot give here the whole app because it is way too large and not relevant.)

$scope.enterRoom = function(info) {
    $q.when(create_room_if_not_exists($scope.room)).then(add_user_to_room($scope.name, $scope.room));
    $location.path("chat");
}


var create_room_if_not_exists = function(room_name) {
    var deferred = $q.defer();
    is_room_already_exists({
        'name': room_name
    }).then(function(response) {
        if (!response.data.is_room_exists) {
            register_room({
                'name': room_name
            });
            console.log("room: " + room_name + ", was created");
            deferred.resolve();
        }
    }, function(error) {
        deferred.reject(error.data);
    });
    return deferred.promise;
}

var add_user_to_room = function(user_name, room_name) {
    console.log(user_name)
    add_user_to_room_request({
        'user_name': user_name,
        'room_name': room_name
    });
}

var is_room_already_exists = function(info) {
    return $http({
        url: '/is_room_already_exists',
        method: 'POST',
        data: info
    });
}

var add_user_to_room_request = function(info) {
    $http({
        url: '/add_user_to_room',
        method: 'POST',
        data: info
    });
}

var register_room = function(info) {
    return $http({
        url: '/register_room',
        method: 'POST',
        data: info
    });
}

What happens is that the 2nd action happens before the 1st one. When I print a log into the console, I see that, and I don't know why.

Both of these actions arrive through an HTTP request to the server - so I don't think the problem is there.

Answers
nr: #1 dodano: 2016-12-31 23:12

Try this:

$scope.enterRoom = function (info) {
  return create_room_if_not_exists($scope.room)).then(function(){
    return add_user_to_room_request({ 'user_name': $scope.name, 'room_name': $scope.name });
  }).then(function(){
    $location.path('chat');
  });
}


var create_room_if_not_exists = function (room_name) {
    var deferred = $q.defer();
    return is_room_already_exists({ 'name': room_name }).then(function (response) {
        if (!response.data.is_room_exists) {
            console.log("room: " + room_name + ", is being created");
            return register_room({ 'name': room_name });
        }
        return response;
    })
}

   var is_room_already_exists = function (info) {
        return $http({
            url: '/is_room_already_exists',
            method: 'POST',
            data: info
        });
    }

 var add_user_to_room_request = function (info) {
            return $http({
                url: '/add_user_to_room',
                method: 'POST',
                data: info
            });
        }

   var register_room = function (info) {
            return $http({
                url: '/register_room',
                method: 'POST',
                data: info
            });
        }
nr: #2 dodano: 2017-01-01 11:01

I am talking about the XHRs not chaining

A common cause of problems with chaining is failure to return promises to the chain:

var add_user_to_room = function(user_name, room_name) {
    console.log(user_name)
    //add_user_to_room_request({
    return add_user_to_room_request({
  //^^^^^^ ----- be sure to return returned promise
        'user_name': user_name,
        'room_name': room_name
    });
}

If chaining is done properly, $q.defer is not necessary:

var create_room_if_not_exists = function(room_name) {
    //var deferred = $q.defer();
    //is_room_already_exists({
    return is_room_already_exists({
  //^^^^^^ --- be sure to return derived promise
        'name': room_name
    }).then(function(response) {
        if (!response.data.is_room_exists) {
            console.log("room: " + room_name + ", to be created");
            //register_room({
            return register_room({
          //^^^^^^ ----- return promise to further chain
                'name': room_name
            });
            //deferred.resolve();
        } else {
            return room_name;
          //^^^^^^ ----- return to chain data
        };
    }, function(error) {
        //deferred.reject(error.data);
        throw error;
      //^^^^^ ------- throw to chain rejection
    });
    //return deferred.promise;
}

If a promise is returned properly, $q.when is not necessary:

$scope.enterRoom = function(info) {
    //$q.when(create_room_if_not_exists($scope.room))
    //   .then(add_user_to_room($scope.name, $scope.room));
    return create_room_if_not_exists($scope.room)
        .then(function() {
            return add_user_to_room($scope.name, $scope.room));
        }).then(function()
            $location.path("chat")
        });
}

The rule of thumb with functional programming is -- always return something.

Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.

-- AngularJS $q Service API Reference - Chaining Promises.

Source Show
◀ Wstecz