Отложенный объект возвращается до разрешения

Я использую библиотеку когда с Node js. Я создаю отложенный объект, помещаю разрешение в инкапсулированную функцию Mongoose findOne() и возвращаю обещание снаружи. Но, похоже, мое обещание всегда возвращается до получения данных.

User.prototype.getProfile = function(criteria) {
    var deferred = when.defer();
    var options = {
        criteria: criteria,
        select: 'name id email'
    };
    this.User.load(options, function(err, data) {
        if (data) {
            this.name = data.name;
            this.email = data.email;
            this.id = data.id;
        } else {
            return false;
        }
        console.log(data);
        deferred.resolve();
    });
    console.log('returning promise');
    return deferred.promise;
};

гость

User.getProfile(req.query).then(
        function success(data) {
            res.send('Hello ' + User.name);// Hello ''
        }
    );

Выходы 'returning promise' перед data

3 ответа

Решение

Да, обещание будет возвращено вызывающей стороне вместо данных, и именно так мы сможем воспользоваться асинхронными функциями. Это обычная последовательность действий при обработке асинхронных вызовов,

  1. Сделайте асинхронный вызов.

  2. Вернуть Promise для звонящего.

  3. На этом этапе звонящему не нужно ждать результата. Он может просто определить then функция, которая знает, что делать, когда данные готовы, и переходить к следующей задаче.

  4. Через некоторое время разрешите (или отклоните, если не удалось) обещание, когда получите результат от асинхронного вызова.

  5. Выполнить then функция на Promise объект, с результатом асинхронного вызова.

Таким образом, ваш код должен быть немного изменен, как это

User.prototype.getProfile = function(criteria) {
    var deferred = when.defer();
    var options = {
        criteria: criteria,
        select: 'name id email'
    };
    this.User.load(options, function(err, data) {
        if (err) {
            // Reject, if there is an error
            deferred.reject(err);
        } else {
            // Resolve it with actual data
            deferred.resolve(data);
        }
    });
    return deferred.promise;
};

Тогда ваш абонент сделает что-то подобное

userObject.getProfile()
    .then(function(profileObject) {
        console.log(profileObject);
        // Do something with the retrieved `profileObject`
    })
    .catch(function(err) {
        console.err("Failed to get Profile", err);
    });

// Do something else here, as you don't have to wait for the data

Здесь звонящий просто звонит getProfile и присоединяет функцию, которая говорит, что делать с возвращенными данными, и переходит.


Редактировать Если вы хотите обновить один и тот же объект, вы можете просто использовать похожий код, но вам нужно сохранить this в какой-то другой переменной, потому что привязка this происходит во время выполнения.

User.prototype.getProfile = function(criteria) {
    var deferred = when.defer();
    var options = {
        criteria: criteria,
        select: 'name id email'
    };
    var self = this;
    this.User.load(options, function(err, data) {
        if (err) {
            // Reject, if there is an error
            deferred.reject(err);
        } else {
            self.name = data.name;
            self.email = data.email;
            self.id = data.id;
        }
        deferred.resolve(data);
    });
    return deferred.promise;
};

Вот как promises Работа.

Поскольку у вас есть асинхронная задача, которая занимает некоторое время, и JavaScript это однопоточный язык, вы не хотите блокировать свой код и ждать завершения этой асинхронной операции - иначе никто бы не использовал JavaScript!!

Ну так что ты делаешь? Вы создаете promise и продолжайте свой код.
Вы добавляете обратные вызовы к этому promise и когда обещание будет выполнено, ваши обратные вызовы будут вызваны.

Я не использовал when библиотека, но то, что вы хотите сделать, это что-то вроде этого:

User.prototype.getProfile = function(criteria){
    var deferred = when.defer();
    var options = {
        criteria : criteria,
        select : 'name id email'
    };
    this.User.load(options, function(err, data) {
        if (data) {
            this.name = data.name;
            this.email = data.email;
            this.id = data.id;
            console.log(data);
             // the callback will invoke after the deferred object is resolved.
             deferred.promise.then(function(o){ console.log('resolved!!!'); });
             deferred.resolve(data);
        }else{
            deferred.reject('something bad occured');
            return false;
        }

    });

    return deferred.promise;
};

Старайтесь избегать использования отложенного шаблона ( см. Здесь).

Когда.js поддерживает шаблон "Выявление конструктора", это также приводит к более читабельному коду.

User.prototype.getProfile = function(criteria) {

    var self = this;
    var options = {
        criteria: criteria,
        select: 'name id email'
    };

    return when.promise(function(resolve, reject) {
        self.User.load(options, function(err, data) {

            if (err) {
                reject(err);
            }

            self.name = data.name;
            self.email = data.email;
            self.id = data.id;

            resolve(); // or resolve(data)
        });
    });
};

следующий

userObject.getProfile()
.then(function() {
    // data loaded
})
.catch(function(err) {
    // something went wrong, process the error
});
Другие вопросы по тегам