在下面的代码中,我有一个无限循环,我不知道它为什么会发生.我最好的猜测是因为里面的函数是异步的,循环不等待它,所以循环永远不会停止.解决这个问题的最佳方法是什么?
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},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一样,这不是因为我已经在我的代码中有一个闭包但它仍然不起作用.这个问题更多的是关于如何将循环变量绑定到闭包)
解决方法
你是不对的,你不能像你想做的那样循环.
Javascript是单线程的.因此,只要主线程在while(循环)语句中循环,NOTHING就有机会运行.如果您的主线程本身正在更改循环变量,那么这一切都会好的,但这并不是正在发生的事情.相反,你试图在异步响应中更改循环变量,但由于你的主线程正在循环,所以不能处理异步响应,因此你的循环变量永远不会被改变,因而是一个非生产性的无限循环.
要修复,您必须更改为不同的循环结构.常见的设计模式是创建一个本地函数,其中包含您想要重复的代码.然后,运行异步操作,如果在异步结果处理程序中,您决定要重复操作,则只需从其中再次调用本地函数.因为结果是异步的,所以堆栈已经展开,这在技术上并不是堆栈构建的递归.它只是启动了该函数的另一个迭代.
我对你的代码中的逻辑感到有点困惑(那里有零注释来解释它)所以我不完全确定我是否正确,但这里是一般的想法:
var generateToken = function(userId) { return new Promise(function(resolve,user) { function find() { var token = Common.randomGenerator(20); User.find({tokens: e},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的整个逻辑似乎很奇怪.逻辑似乎很奇怪,它闻起来像“在紧密循环中轮询数据库”,这通常是一个非常糟糕的表现.我怀疑如果我们在更高层次上理解整体问题,有更有效的方法来解决这个问题.