【问题标题】:When does a promise's then method become fulfilled or rejected?Promise 的 then 方法何时实现或拒绝?
【发布时间】:2015-11-07 16:07:36
【问题描述】:

promise 上的 then() 方法返回一个 promise。 Promise 可以处于以下三种状态之一:挂起、已完成或已拒绝。但是,在创建承诺时,当您希望您的承诺被履行或拒绝时,您有一个已解决和被拒绝的方法可以调用。我不知道如何在 then() 方法中的函数上使用这些方法。当我尝试在 then() 链中间使用带有回调的普通异步函数时,这给我带来了问题。以这段代码为例。

        User.findOne({
            where: {
                email: email
            }
        }).then(function(user){
            if(user){
                return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
            }else{
                var user = User.build({ email: email });
                console.log("ONE");
                User.generateHash(password, function(err, hash){
                    console.log("TWO");
                    if(err){
                        return done(err);
                    }else{
                        user.password = hash;
                        var newUser = user.save();
                        return newUser;
                    }
                })
            }
        }, function(err){
            console.log("ERROR: ", err);
        }).then(function(newUser){
            console.log("THREE");
            return done(null, newUser);
        }, function(err){
            console.log("USER NOT CREATED: ", err);
        });

在这种情况下,User.findOne() 返回一个承诺。现在控制台将打印一三二。一旦调用异步,就会调用第二个 then 语句。有没有办法在我通知它已解决之前不调用第二个 then 语句。

(我知道这将是修复几种不同方式的简单代码,我更好奇为什么以及何时实现第一个语句返回的 then 承诺。)

【问题讨论】:

  • “有没有办法在我通知它已解决之前不调用第二个 then 语句” js 的哪一部分引用? ,初始.then() ?,done() 叫什么? “在 then() 方法中找不到如何在函数上使用这些方法” 从初始调用返回的承诺 User.findOne 已经实现或拒绝;可以在初始 .then() 内创建一个新的 Promise

标签: javascript asynchronous promise bluebird


【解决方案1】:

所以发生的情况是,findUser 得到满足(而不是被拒绝),所以.then() 的第一个参数中的回调调用(而不是第二个)。 在.then() 子句中,你console.log("ONE") 并且你没有返回任何东西(因为你属于else { 子句,并且你只在回调中返回createHash 这在上下文中没有多大意义承诺。),所以它实际上返回undefined

然后下一个.then() 调用,你得到console.log("THREE")

然后在未来的某个时候,您会从调用的createHash 获得回调,然后您会获得console.log("TWO")

正确的方法是编写 User.generateHash 以便它也返回一个 Promise,然后您可以将它与 Promise 链的其余部分链接起来,而不必在您的 Promise 回调中处理异步内容。

【讨论】:

  • 好的,所以 then() 承诺在函数返回时实现。如果什么都没有返回,那么一旦它运行它的所有代码就完成了。谢谢
【解决方案2】:

在创建一个 Promise 时,当您希望实现或拒绝您的 Promise 时,您可以调用一个已解决和已拒绝的方法。我不知道如何在 then() 方法中的函数上使用这些方法

在 Promise 上使用 then 方法时,您无法访问 resolve/reject 回调 - then 为您管理所有这些。它确实返回了新的 Promise,它将用你的回调的 result 解决,即它的 return value。这个返回值可以是一个会被自动吸收的promise(当你用一个promise解决它时,它会自动用这个promise的结果来实现/拒绝)。

所以这就是你需要在这里做的。当你想在then回调中调用一个回调方法时,你必须为它创建一个promise,然后你可以return。最简单和最好的方法是事先使用promisify the callback function,但您也可以使用Promise 构造函数(其中您可以访问resolve/reject)。

    User.findOne({
        where: {
            email: email
        }
    }).then(function(user){
        if (user){
            throw new Error('That email is already taken.');
        } else {
            var user = User.build({ email: email });
            console.log("ONE");
            var promise = new Promise(function(resolve, reject) {
//                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                User.generateHash(password, function(err, hash){
                if (err) reject(err);
                else     resolve(hash);
            });
            return promise.then(function(hash) {
                console.log("TWO");
                user.password = hash;
                var newUser = user.save();
                return newUser;
            });
        }
    }).then(function(newUser){
        console.log("THREE");
        return done(null, newUser);
    }, function(err){
        console.log("ERROR: ", err);
        return done(null, false, req.flash('signupMessage', err.message));
    });

【讨论】:

  • 请不要嵌套.then()s。只是回报承诺。如果您迫切需要范围的东西并且不能使用像 bluebird 这样的库来理智地做到这一点,那么只需 resolve({hash, user}) 或类似的东西。
  • @Madara 嗯,我想你可以简单地使用unnest the then(或多或少,也许在其他地方创建user),但我想保留 OP 的代码结构,并明确想要展示只有回调 promisification 应该进入 Promise 构造函数,其他所有内容都在单独的 then 回调中。
  • 您的代码有两个 Promise 错误做法(.then()s 的嵌套和在 .then() 中使用 Promise 构造函数),我知道您希望保持代码相似,但是这些不好的做法,应该在示例代码中。
  • @MadaraUchiha:不。嵌套then 在不必要的时候可能是一种气味,但并不是一个坏习惯。在then 内有一个Promise 构造函数绝对没有错 - 将它围绕 thencommon antipattern
【解决方案3】:

如果 done() 和 User.generateHash() 不返回承诺,那么这个程序的第二级没有理由在进入下一个 then() 块之前等待。

要解决这个问题,或者看到您的打印语句打印出一二三,我会这样做

User.findOne({
    where: {
        email: email
    }
}).then(function(user){
    var promise;
    if(user){
        promise = done(null, false, req.flash('signupMessage', 'That email is already taken.'));
    }else{
        var user = User.build({ email: email });
        console.log("ONE");
        promise = User.generateHash(password, function(err, hash){
            return new Promise(function(resolve, reject){
                console.log("TWO");
                if(err){
                    reject(done(err));
                }else{
                    user.password = hash;
                    var newUser = user.save();
                    resolve(newUser);
                }
            });
        });
    }

    return promise;
}, function(err){
    console.log("ERROR: ", err);
}).then(function(newUser){
    console.log("THREE");
    return done(null, newUser);
}, function(err){
    console.log("USER NOT CREATED: ", err);
});

这样,第一个 then 将在进入下一个 then 块之前等待 promise 被解析,这意味着 User.generateHash 必须在进入下一个 then 块之前解析,因为变量 promise 依赖于它。

【讨论】:

  • User.generateHash 看起来不像返回一个承诺 - 它具有典型的节点回调外观
  • 你是对的,必须进行修改以使其返回承诺,这是最佳答案所暗示的。
  • 顺便说一句,我并没有反驳你的答案,只是回答了你答案中最初的 question :p
猜你喜欢
  • 1970-01-01
  • 2019-08-27
  • 2017-05-27
  • 2016-08-21
  • 2019-04-17
  • 2016-11-10
  • 1970-01-01
  • 2020-12-22
  • 1970-01-01
相关资源
最近更新 更多