Цепочка нескольких обещаний (обработка обратных вызовов)
У меня возникают некоторые трудности с обещаниями, когда речь идет о цепочке из нескольких. Путаница заключается в том, как правильно использовать обещания и их разницу с обратными вызовами. Я заметил, что обратные вызовы иногда запускаются независимо от того, решено ли обещание или нет, что делает приведенную ниже реализацию ненадежной..(Если мой синтаксис и логика неверны)
Я прочитал официальную документацию и придумал это, но я не уверен, что она хорошо реализована.
Процесс регистрации выглядит следующим образом:
- Пользователь выбирает псевдоним -> Подробности. Псевдоним + идентификатор пользователя (универсальный уникальный идентификатор устройства) отправляются на серверную сторону.
- Если псевдоним доступен, генерируется 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)
- независимо от того, когда обещание было или будет разрешено или отклонено, затем асинхронно вызывает один из обратных вызовов об успехе или ошибке, как только результат станет доступен. Обратные вызовы вызываются с одним аргументом: результатом или причиной отклонения. Кроме того, обратный вызов уведомлений может вызываться ноль или более раз для предоставления индикации хода выполнения, прежде чем обещание будет разрешено или отклонено.
- Какой смысл иметь обратные вызовы, если они могут срабатывать независимо от того, выполнено ли обещание?
- Разве я не должен звонить, например, 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);
});