【问题标题】:deferred object returning before resolving在解析之前返回的延迟对象
【发布时间】:2015-05-01 15:41:12
【问题描述】:

我正在使用带有 Node js 的 when 库。我创建了一个延迟对象,将解析放在封装的 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 ''
        }
    );

data之前输出'returning promise'

【问题讨论】:

  • 这是预期的结果。您对代码有任何问题吗?请注意,您的return false 将意味着承诺永远不会解决/拒绝,这很糟糕。
  • 是的,这正是重点。如果User.load 阻止执行并等待查询完成,那么一开始就没有任何意义。
  • 这看起来有点像deferred antipattern - 你从来没有reject,只是让承诺待定。

标签: javascript node.js promise deferred when-js


【解决方案1】:

这就是promises 的工作原理。

由于您有一个需要一些时间的异步任务,并且JavaScript 是一种单线程语言,因此您不想阻塞您的代码并等待该异步操作自行完成 - 否则没有人会使用JavaScript !!

那你是做什么的?您创建一个 promise 并继续您的代码。
您向该 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;
};

【讨论】:

  • 你的代码和 OP 的数据有同样的问题,如果 data 是假的,promise 永远不会解决。
【解决方案2】:

是的,promise 将返回给调用者而不是数据,这就是我们可以利用异步函数的方式。这是处理异步调用的常见动作序列,

  1. 进行异步调用。

  2. 向调用者返回Promise

  3. 此时,调用者不必等待结果。它可以简单地定义一个then 函数,它知道当数据准备好时该做什么并继续下一个任务。

  4. 稍后,当您从异步调用中获得结果时,解决(或拒绝,如果失败)promise。

  5. Promise 对象执行 then 函数,结果来自异步调用。

所以,你的代码需要稍微修改一下,像这样

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

【讨论】:

    猜你喜欢
    • 2012-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-12
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多