在下面的代码中,我有一个无限循环,我不知道它为什么会发生。我最好的猜测是因为里面的函数是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相同而不是因为我的代码中已经有一个闭包但它仍然无法正常工作。那个问题更多的是关于如何将循环变量绑定到闭包)
答案 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基础知识的人都可以理解,几乎可以想象的任何异步迭代风格,并且被广泛使用和非常好记录。