在循环中承诺 [英] Promise inside a loop

查看:76
本文介绍了在循环中承诺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In the following code, I have an infinite loop which I don't know why it happens. My best guess is because the function inside is async the loop doesn't wait for it and so the loop never stops. What is the best way to solve this issue ?

 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 function receives a userId (User.findOne() is used to find the user and if there's no user with that id , reject the promise) and creates a unique random token for that user (randomGenerator) , add it to the user entity which is kept in MongoDB and the return it back to the caller.

注意有一些投票表明这个问题与这个这不是因为我的代码中已经有一个闭包但它仍然不起作用。这个问题更多的是关于如何将循环变量绑定到闭包)

(NOTE There were some down votes saying this question is same as this one Which is not as I already had a closure in my code and it still doesn't work. That question was more about how to bind the looping variable to the closure)

推荐答案

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

You are correct that you cannot loop like you are trying to do.

Javascript是单线程的。因此,只要主线程在 while(循环)语句中循环,NOTHING else就有机会运行。如果您的主线程本身正在更改循环变量,那么这一切都会好的,但这并不是正在发生的事情。相反,您试图在异步响应中更改循环变量,但由于您的主线程正在循环,因此不能处理异步响应,因此您的 loop 变量永远不会被改变,因而是一个非生产性的无限循环。

Javascript is single threaded. As such as long as the main thread is looping in your while(loop) statement, NOTHING else gets a chance to run. This would all be OK if your main thread itself was changing the loop variable, but that isn't exactly what is happening. Instead, you're trying to change the loop variable in an async response, but because your main thread is looping, the async response can NEVER be processed so thus your loop variable can never get changed and thus a non-productive infinite loop.

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

To fix, you will have to change to a different looping construct. A common design pattern is to create a local function with the code in it that you want to repeat. Then, run your async operation and if, in the async result handler, you decide you want to repeat the operation, you just call the local function again from within it. Because the result is async, the stack has unwound and this isn't technically recursion with a stack build up. It's just launching another iteration of the function.

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

I am a bit confused by the logic in your code (there are zero comments in there to explain it) so I'm not entirely sure I got this correct, but here's the general idea:

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上有不完整的错误处理( )操作。

I should note that you have incomplete error handling on the User.findOne() operation.

FYI,持续查询的整个逻辑,直到你得到 result.length === 0 看起来很奇怪。逻辑似乎很奇怪,它闻起来像在紧密循环中轮询数据库,这通常是一个非常糟糕的表现。我怀疑如果我们将整体问题理解到更高的水平,有更有效的方法可以解决这个问题。

FYI, the whole logic of continuously querying until you get result.length === 0 just seems bizarre. The logic seems odd and it smells like "polling a database in a tight loop" which is usually a very poor performing thing to do. I suspect there are much more efficient ways to solve this problem if we understood the overall problem at a higher level.

这篇关于在循环中承诺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆