javascript - 循环内的 promise

标签 javascript node.js promise

在下面的代码中,我有一个无限循环,我不知道为什么会这样。我最好的猜测是因为里面的函数是 async 循环不会等待它,所以循环永远不会停止。解决此问题的最佳方法是什么?

 var generateToken = function(userId) {
    return new Promise(function(resolve, reject) {
        User.findOne({userId: userId}, function(err, user) {
            if (user !== null) {
                var loop = true;
                while (loop) {
                    var token = Common.randomGenerator(20);
                    (function(e) {
                        User.find({tokens: e}, function(err, result) {
                            if (err) {
                                loop = false;
                                reject('Error querying the database');
                            } else {
                                if (result.length === 0) {
                                    if (user.tokens === undefined) {
                                        user.tokens = [];
                                    }
                                    user.tokens.push(e);
                                    loop = false;
                                    resolve();
                                }
                            }
                        });
                    })(token);
                }
            } else {
                return reject('UserNotFound');
            }
        });
    });
};

此函数接收一个 userId(User.findOne() 用于查找用户,如果没有具有该 id 的用户,则拒绝 promise )并创建一个唯一 该用户的随机 token (randomGenerator) ,将其添加到保存在 MongoDB 中的用户实体中,并将​​其返回给调用者。

(注意 有一些反对票说这个问题与 this one 相同,这不是因为我的代码中已经有一个闭包而且它仍然不起作用。这个问题更多关于如何将循环变量绑定(bind)到闭包)

最佳答案

你是对的,你不能像你想做的那样循环。

Javascript 是单线程的。只要主线程在您的 while(loop) 语句中循环,其他任何东西都没有机会运行。如果您的主线程本身正在更改 loop 变量,这一切都没有问题,但事实并非如此。相反,您试图在异步响应中更改 loop 变量,但是由于您的主线程正在循环,因此永远无法处理异步响应,因此您的 loop 变量永远不会改变,因此是一个非生产性的无限循环。

要修复,您必须更改为不同的循环结构。一种常见的设计模式是创建一个本地函数,其中包含您要重复的代码。然后,运行异步操作,如果在异步结果处理程序中,您决定要重复该操作,则只需从其中再次调用本地函数即可。因为结果是异步的,堆栈已经展开,这在技术上不是堆栈构建的递归。它只是启动该函数的另一个迭代。

我对你代码中的逻辑有点困惑(那里有零条评论来解释它)所以我不完全确定我的理解是否正确,但这是一般的想法:

var generateToken = function(userId) {
    return new Promise(function(resolve, reject) {
        User.findOne({userId: userId}, function(err, user) {
            function find() {
                var token = Common.randomGenerator(20);
                User.find({tokens: e}, function(err, result) {
                    if (err) {
                        reject('Error querying the database');
                    } else {
                        if (result.length === 0) {
                            if (user.tokens === undefined) {
                                user.tokens = [];
                            }
                            user.tokens.push(e);
                            resolve();
                        } else {
                            // do another find until we don't find a token
                            find();
                        }
                    }
                });
            }

            if (user !== null) {
                find();
            } else {
                reject('UserNotFound');
            }
        });
    });
};

我应该注意到您对 User.findOne() 操作的错误处理不完整。

仅供引用,不断查询直到获得 result.length === 0 的整个逻辑看起来很奇怪。这个逻辑看起来很奇怪,而且闻起来像“在一个紧密的循环中轮询数据库”,这通常是一件性能很差的事情。如果我们在更高层次上理解整个问题,我怀疑会有更有效的方法来解决这个问题。

关于javascript - 循环内的 promise ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31947933/

相关文章:

javascript - 如何在 Javascript 中返回 HTTP 响应?

node.js - 在 package.json 中添加 bower 作为 devDependency

javascript - 获取promise中的返回函数值

javascript - 代码始终在响应发送到浏览器后执行

javascript - 在 IF 语句中使用异步函数的返回值

javascript - 如何从 Javascript 设置 JSP 中文本字段的值

javascript - 禁用提交按钮,直到至少选中一个复选框

javascript - Node - 无数据创建的 ES 索引

node.js - 从 Node js同步重命名文件

angularjs - 再次解决已解决的 promise