在循环中承诺

时间:2015-08-11 17:11:04

标签: 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的用户,则拒绝承诺)并创建唯一随机令牌对于该用户(randomGenerator),将其添加到保存在MongoDB中的用户实体,并将其返回给调用者。

注意有一些投票表明这个问题与this one相同而不是因为我的代码中已经有一个闭包但它仍然无法正常工作。那个问题更多的是关于如何将循环变量绑定到闭包)

2 个答案:

答案 0 :(得分:6)

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

Javascript是单线程的。因此,只要主线程在loop语句中循环,NOTHING就有机会运行。如果您的主线程本身正在更改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操作上的错误处理不完整。

仅供参考,在获得.btn-group-vertical>.btn-group:after, .btn-group-vertical>.btn-group:before, .btn-toolbar:after, .btn-toolbar:before, .clearfix:after, .clearfix:before, .container-fluid:after, .container-fluid:before, .container:after, .container:before, .dl-horizontal dd:after, .dl-horizontal dd:before, .form-horizontal .form-group:after, .form-horizontal .form-group:before, .modal-footer:after, .modal-footer:before, .nav:after, .nav:before, .navbar-collapse:after, .navbar-collapse:before, .navbar-header:after, .navbar-header:before, .navbar:after, .navbar:before, .pager:after, .pager:before, .panel-body:after, .panel-body:before, .row:after, .row:before { display: table; content: ''; } 之前不断查询的整个逻辑似乎很奇怪。逻辑似乎很奇怪,它闻起来像“在紧密循环中轮询数据库”,这通常是一个非常糟糕的表现。我怀疑如果我们在更高层次上理解整体问题,有更有效的方法来解决这个问题。

答案 1 :(得分:1)

就学习如何解决此类问题而言,您可能需要查看 async 库(https://github.com/caolan/async)。它提供了非常直观的方式来处理这样的异步情况,大多数熟悉同步迭代和javascript基础知识的人都可以理解,几乎可以想象的任何异步迭代风格,并且被广泛使用和非常好记录。