Цепочка нескольких обещаний (обработка обратных вызовов)

У меня возникают некоторые трудности с обещаниями, когда речь идет о цепочке из нескольких. Путаница заключается в том, как правильно использовать обещания и их разницу с обратными вызовами. Я заметил, что обратные вызовы иногда запускаются независимо от того, решено ли обещание или нет, что делает приведенную ниже реализацию ненадежной..(Если мой синтаксис и логика неверны)
Я прочитал официальную документацию и придумал это, но я не уверен, что она хорошо реализована.

Процесс регистрации выглядит следующим образом:

  • Пользователь выбирает псевдоним -> Подробности. Псевдоним + идентификатор пользователя (универсальный уникальный идентификатор устройства) отправляются на серверную сторону.
  • Если псевдоним доступен, генерируется ApiKey(токен), пользователь регистрируется и отправляется обратно клиентской стороне (хранится в БД)

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));


Теперь, в моем контроллере, я хотел бы связать оба обещания выше. Я цитирую следующее из документации:

then(successCallback, errorCallback, notifyCallback) - независимо от того, когда обещание было или будет разрешено или отклонено, затем асинхронно вызывает один из обратных вызовов об успехе или ошибке, как только результат станет доступен. Обратные вызовы вызываются с одним аргументом: результатом или причиной отклонения. Кроме того, обратный вызов уведомлений может вызываться ноль или более раз для предоставления индикации хода выполнения, прежде чем обещание будет разрешено или отклонено.

  1. Какой смысл иметь обратные вызовы, если они могут срабатывать независимо от того, выполнено ли обещание?
  2. Разве я не должен звонить, например, Promise2 в рамках первого обратного вызова Promise? Если он запускается независимо от разрешения Promise1, как я могу связать Promise2 таким образом, чтобы он срабатывал только при разрешении Promise1?


Что я пробовал:
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');
                    });
  }]);

Примечания: я понимаю, что хранить детали на стороне клиента - плохая практика, однако я придерживаюсь другой концепции (анонимный обмен сообщениями) и проблем безопасности нет.

Спасибо за ваше время

2 ответа

Решение

Ваш второй тогда звонок кажется неверным, после

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

then принимает до 3 параметров then(successCallback, errorCallback, notifyCallback) Вы передаете это: deviceDB.RegDevice($scope.alias, $scope.apiKey, $scope.Device) который оценивается немедленно и возвращаемое обещание передается функции then в качестве функции успеха ваша функция успеха передается как errorCallback, а функция сбоя - как notifyCallback.

Я бы попробовал следующее

 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 */}

Обратите внимание, что вызов RegDevice теперь находится внутри функционального блока, и обещание возвращается из блока then, из которого вы хотите создать цепочку.

Я считаю $q.serial отличной библиотекой для создания цепочек обещаний. Он очень прост в использовании и обрабатывает множество вещей, таких как проверка, являются ли все обещания в цепочке действительно обещаниями.

Вот небольшой пример:

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!!!");                       
        });                                                                    
}  

Вот подход, который может быть полезен с использованием 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);
    });
Другие вопросы по тегам