简体   繁体   中英

Chaining multiple promises (handling callbacks)

I am having some difficulties with promises when it comes chaining multiple ones. The confusion is distinguishing how to properly take advantage of promises & their difference with Callbacks . I noticed that callbacks sometime fire regardless a promise is resolved or not, making the below implementation unreliable..(Unless my syntax & logic are wrong)
I read the official documentation and came up with this, but I am not sure it is well implemented.

The Registration flow is as follow:

  • User chooses an Alias -> Details Alias + userID (Device's Universally Unique Identifier) are sent server side
  • If Alias is available, ApiKey(token) is generated, User registered and sent back client side (Stored in DB)

Services.js

(function(angular) {
   myApp.factory("deviceDB.Service", ['$resource', '$http', '$q',
   function ($resource,  $http , $q ) {

    return {

//Second Promsie : After API token is generated server-side, store res in db

        RegDevice: function (alias, apiKey, userID) { 
            var deferred = $q.defer();
            var configuration ;
            var db = window.sqlitePlugin.openDatabase({name: "config.db"});
            setTimeout(function () {

                db.transaction(function (tx) {
                    tx.executeSql('CREATE TABLE IF NOT EXISTS user_details (userID UNIQUE , alias TEXT, apiKey TEXT)');
                    tx.executeSql("INSERT INTO user_details (userID, alias, apiKey) VALUES (?,?,?)", [userID, alias, apiKey], function (tx, res) {

                        deferred.resolve(configuration = true);
                    }, function (e) {
                        // console.log("ERROR: " + e.message);
                        deferred.reject(configuration = false);
                    });
                });

            }, 1000);
            return deferred.promise;
        },

//First Promsie: Register user  server side & generate APi token

        RegUser: function (alias, userID) { 

            var deferred = $q.defer();
            var pro;
            pro = $resource('api/query/register', {'alias': alias, 'userID': userID},
                { query: {
                        isArray: false,
                        method: 'GET'  } });

            setTimeout(function () {
                pro.query(function (res) {
                    if (res.error) {
                        deferred.reject( { error : res.error, exists: res.exists,  msg: res.message } );
                    }
                    else {
                        deferred.resolve( {error : res.error , alias: res.alias , apiKey: res.apiKey, msg: res.message } );
                    }
                }, function (e) {
                    deferred.reject( { errorStatus: e.status } );

                });

            }, 1000);
            return deferred.promise;
        }

    };

  }]);

}(window.angular));


Now, in My controller I would like to chain both promises above. I quote the follwoing from the Documentation :

then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.

  1. What is the point of having Callbacks if they can fire regardless if the Promise is resolved?
  2. Shouldn't I call for eg Promise2 within the first Promise's Success Callback? If it is fired regardless of of Promise1 being resolved, How then can I chain Promise2 in a way to fire only when Promise1 is resolved ?


What I tried :
Controller.js

myApp.controller('RegisterController', ['$scope', '$http', 'deviceDB.Service',
    function ($scope , $http , deviceDB.Service) {

   var Promise1 = deviceDB.RegUser($scope.alias, $scope.Device); 

// First promise - Validate with server
   Promise1.then(function(data)
                    {
                        console.log(' Registration Server-Side successfully');
                        $scope.apiKey = data.apiKey;
                        term.echo(data.apiKey);

                    }, function(e)
                    {
                        console.log('Registration Failed');
                        term.echo(e.msg);

                    })

//Call Promise 2 & Store details Client-Side using .then()

    .then(deviceDB.RegDevice($scope.alias, $scope.apiKey, $scope.Device), 
     function(d){
                       console.log('Items Stored in DB successfully');
                    }, function()
                    {
                        console.log('Items Stored in DB Failed');
                    });
  }]);

Notes: I understand it is a bad practice to store details client-side, however, i am after a different concept (anonymous messaging) and there is no security concerns..

Thanks for your time

Your second then call seems incorrect, after

//Call Promise 2 & Store details Client-Side using .then()

then takes up-to 3 parameters then(successCallback, errorCallback, notifyCallback) you are passing it: deviceDB.RegDevice($scope.alias, $scope.apiKey, $scope.Device) which is evaluated immediately and the promise returned is passed to the function then as the success function, your success function is passed as the errorCallback and your fail function is passed as the notifyCallback.

I would try the following

 Promise1.then(function(data)
 {
   console.log(' Registration Server-Side successfully');
   $scope.apiKey = data.apiKey;
   term.echo(data.apiKey);

   return deviceDB.RegDevice($scope.alias, $scope.apiKey, $scope.Device)

 }, function(e)
 {
   console.log('Registration Failed');
   term.echo(e.msg);

   return e;

 }).then(function(d) {/*all good*/}, function(e) {/* all bad */}

Notice the call to RegDevice is now within a function block, and a promise is returned from the then block you want to chain from.

I find $q.serial a great library for chaining promises. It's very easy to use and handles a lot of stuff like checking if all promises on the chain are really promises.

Here is a small example:

function do_all() {                                                         
    var task_1 = function() {                                                  
        return $http.get("some url")                                           
            .then(on_xhr_completed_fn, on_xhr_failed_fn);                      
    }                                                                          

    var task_2 = function(some_data) {                                         
        vm.bla = some_data;                                                    
        return $http.get("other url")                                          
            .then(on_xhr_completed_fn, on_xhr_failed_fn);                      
    }                                                                          

    var task_3 = function(other_data) {                                        
        vm.bli = other_data;                                                   
    }                                                                       

    var tasks = [task_1, task_2, task_3];                                      

    return $q.serial(tasks)                                                    
        .then(function() {                                                     
            console.log("Finished tasks 1, 2 and 3!!!");                       
        });                                                                    
}  

Here's an approach that may be helpful using async/await:

async function run_promise_A(args) {
    return new Promise((resolve, reject) => {
        return resolve(resolve_value)
    });
}

async function run_promise_B(args) {
    return new Promise((resolve, reject) => {
        return resolve(resolve_value)
    });
}

async function run_promise_C(args) {
    return new Promise((resolve, reject) => {
        return resolve(resolve_value)
    });
}

async function run_several_async_functions(userid) {
    let a = run_promise_A(userid);
    let b = run_promise_B(a);
    let c = run_promise_C(b);
    return c;
}

return Promise.resolve()
    .then(() => {
        let c = (async () => {
            let c = await run_several_async_functions(userid)
            return c;
        })();
        return c;
    })
    .then((c) => {
        return c;
    })
    .catch((err) => {
        console.log(err);
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM