在一个循环内承诺

在下面的代码中,我有一个无限循环,我不知道为什么会发生。 我最好的猜测是因为里面的函数是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),并为该用户( randomGenerator )创build一个唯一的随机标记,将其添加到用户实体保存在MongoDB中,并返回给调用者。

注意有些人反对说这个问题与这个问题是一样的, 这不是我已经在我的代码中有一个闭包,它仍然无法正常工作,这个问题更多地是关于如何将循环variables绑定到闭包)

你是正确的,你不能像你正在做的循环。

Javascript是单线程的。 只要主线程在while(loop)语句中while(loop) ,其他任何人都有机会运行。 如果你的主线程本身正在改变loopvariables,这一切都会好的,但这并不是正在发生的事情。 相反,你试图在asynchronous响应中改变loopvariables,但是因为你的主线程正在循环,所以asynchronous响应永远不能被处理,所以你的loopvariables永远不会被改变,因此是一个非生产性的无限循环。

要解决,你将不得不改变到一个不同的循环构造。 一个常见的devise模式是用你想要重复的代码创build一个本地函数。 然后,运行asynchronous操作,如果在asynchronous结果处理程序中决定要重复该操作,则只需从其中再次调用本地函数即可。 因为结果是asynchronous的,所以堆栈已经解开了,这在技术上并不是堆栈堆积的recursion。 这只是启动另一个function的迭代。

我对你的代码中的逻辑有点困惑(在这里有零个注释来解释它),所以我不完全确定我得到了这个正确的,但是这里的总体思路是:

 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()操作上有不完整的error handling。

FYI,不断查询,直到你得到result.length === 0的整个逻辑似乎离奇。 这个逻辑似乎很奇怪,闻起来像是“在一个紧密的循环中轮询数据库”,这通常是一个非常糟糕的performance。 我怀疑,如果我们能够更高层次地理解整个问题,那么解决这个问题的方法就更有效了。

至于学习如何解决这个问题,你可能想看看asynchronous库( https://github.com/caolan/async )。 它提供了非常直观的方式来处理这种asynchronous情况,大多数熟悉同步迭代和JavaScript基础知识的人都可以理解这种方式,几乎可以想象任何asynchronous迭代的风格,并且被广泛使用,非常好logging。