Как работать с цепочками промисов

Я немного смущен, как мне обращаться с обещаниями в моей ситуации.

Это моя фабрика:

return {
  getCategory: function(categoryId) {
    var ref = firebase.database().ref().child('categories').child(categoryId);
    var category = $firebaseObject(ref);
    return category.$loaded().then(function() {
        return category;
    });
  },
  getEntry: function(categoryId, entryId) {
    var ref = firebase.database().ref().child('entries').child(categoryId).child(entryId);
    var entry = $firebaseObject(ref);
    return entry.$loaded().then(function() {
        return entry;
    });
  }
}

На моей фабрике я стараюсь не делать так:

var d = $q.defer();
if() {
    d.resolve();
}
return d.promise;

Потому что $loaded() возвращает обещание.

А это мой контроллер:

var categoryId = 'abc';
var entryId = 'def';
// so here i'm getting the category
MyFactory.getCategory(categoryId)
.then(function(category) {
    if(category.$value === null)
    {
        $scope.error = 'The category does not exist';
    }
    else if(new Date() > new Date(category.expireDate))
    {
        $scope.error = 'The category has expired';
    }
    else {
        $scope.category = category;
        // if no errors
        return MyFactory.getEntry(category.$id, entryId);
    }
})
.then(function(entry) {
    if(entry.$value === null)
    {
        $scope.error = 'No such entry';
    }
    else {
        $scope.entry = entry;
    }
})
.catch(function(error) {
    console.error(error);
});

Чего я хочу добиться, так это сначала получить категорию, а затем, есть ли какие-то ошибки или нет, получить запись соответственно. Данные поступают из базы данных Firebase.

Это работает, однако я не совсем уверен, как мне обрабатывать обещание, когда я хочу сделать следующее .then и не вкладывать их одно в другое, как это:

MyFactory.getCategory().then(function(category) {
    if(no category errors) {
        MyFactory.getEntry().then(function() {
            // ...
        });
    }
});

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

Итак, реальный вопрос заключается в том, как мне с этим справиться правильно, чтобы он работал должным образом? Спасибо.


person Rafff    schedule 26.05.2017    source источник


Ответы (1)


Вы должны вернуть отклоненное обещание, когда есть ошибка.

Посмотрите на следующий пример:

MyFactory
    .getCategory(categoryId)
    .then(function (category) {
        if (category.$value === null) {
            return $q.reject('The category does not exist');
        }
        if (new Date() > new Date(category.expireDate)) {
            return $q.reject('The category has expired');
        }
        $scope.category = category;
        return MyFactory.getEntry(category.$id, entryId);
    })
    .then(function (entry) {
        if (entry.$value === null) {
            return $q.reject('No such entry');
        }
        $scope.entry = entry;
    })
    .catch(function (error) {
        $scope.error = error;
    });

Не забудьте ввести $q в свой контроллер.

Изменить

Я также предлагаю вам перенести «логику ошибок» в вашу службу, чтобы контроллер всегда получал либо данные в .then(data => { ... }), либо строку ошибки в .catch(error => { ... }). Это сделает ваши контроллеры чище, и если вы используете этот сервисный метод в другом контроллере, вам также не придется копировать свою логику там.

Сервис

return {
    getCategory: function(categoryId) {
        var ref = firebase.database().ref().child('categories').child(categoryId);
        var category = $firebaseObject(ref);
        return category.$loaded().then(function() {
            if (category.$value === null) {
                return $q.reject('The category does not exist');
            }
            if (new Date() > new Date(category.expireDate)) {
                return $q.reject('The category has expired');
            }
            return category;
        });
    },
    getEntry: function(categoryId, entryId) {
        var ref = firebase.database().ref().child('entries').child(categoryId).child(entryId);
        var entry = $firebaseObject(ref);
        return entry.$loaded().then(function() {
            if (entry.$value === null) {
                return $q.reject('No such entry');
            }
            return entry;
        });
    }
}

Контроллер

MyFactory
    .getCategory(categoryId)
    .then(function (category) {
        $scope.category = category;
        return MyFactory.getEntry(category.$id, entryId);
    })
    .then(function (entry) {
        $scope.entry = entry;
    })
    .catch(function (error) {
        $scope.error = error;
    });
person Vladimir Zdenek    schedule 26.05.2017
comment
О Боже! Это мне очень помогло. Спасибо. Но опять небольшая путаница. Вы используете $q.reject в службе. Какое обещание на самом деле отвергается? Поскольку вы не делаете $q.defer()? - person Rafff; 26.05.2017
comment
Я рад, что смог помочь!. $q.reject() — это ярлык, который создает и отклоняет промис за один шаг. Вот официальная документация - docs.angularjs.org/api/ng/service /$q#отклонить - person Vladimir Zdenek; 26.05.2017