【问题标题】:Infinite loop in NodejsNodejs 中的无限循环
【发布时间】:2018-07-12 18:16:24
【问题描述】:

在使用 nodejs 制作链接 Shortner 脚本时,我遇到了以下问题: 由于我忽略的原因,我的程序进入了无限循环 这是代码:

function makeShort() {
    var short = "";
    var cond = true;

    while(cond){
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

        for(var i = 0; i < length; i++){
            short += possible.charAt(Math.floor(Math.random() * possible.length));
        }

        let query = {short:short};

        Link.findOne(query, (err, link)=>{
            if(err) throw err;
            if(!link){
                console.log("here");
                cond = false;

            }
        });
    }
    return short;
}

然后在这里使用它:

router.post('/', (req, res)=>{
    let short = makeShort();
    const newLink = new Link({
        url: req.body.url,
        short:short
    });

    newLink.save().then(link => {
        res.json(link);
    });
});

我的想法是我生成一个随机字符串(5 个字符),然后,如果它存在,我创建另一个,依此类推.. 直到我找到一个未使用的字符串(数据库是空的 btw 所以有没有理由让它无限循环)。

【问题讨论】:

  • 它在for 循环中无限循环,因为length 没有定义
  • 所以将 length 中的 for 循环更改为 5 或您希望字符串的其他长度
  • 看起来 cond = true 超出了 var cond = false 的范围
  • 我可能错了,但 findOne 不会被多次调用,直到 cond == false 因为它运行回调?
  • 另外,你可以用这个替换你的 for 循环:short = (Math.random() * 1000).toString(32).replace(/\./g, '').substr(0, length)

标签: javascript node.js loops infinite


【解决方案1】:

您可以使用async/await 循环并测试数据库中的值。我们所做的是将您的函数转换为异步函数,然后创建一个新函数,该函数将返回一个将解析true/false 的承诺。

接下来,我们在 while 循环中调用该函数,并在 await 中调用包含 true/false 的结果,然后将其设置为变量 cond 并继续循环。

看起来像这样:

async function makeShort(length) {
  let cond = true;

  while (cond) {
    let short = (Math.random() * 1000).toString(32).replace(/\./g, '').substr(0, length);

    let query = { short: short };
    cond = await findOne(query);
  }
  return short;
}

function findOne(query) {
  return new Promise(resolve => {
    Link.findOne(query, (err, link) => {
      if (err) resolve(false);
      if (!link) {
        return resolve(false);
      }
      return resolve(true);
    });
  })
}

然后我们可以像这样使用let short = await makeShort() 调用它(我们还必须生成(req, res) 函数async):

router.post('/', async (req, res) => {
  let short = await makeShort();
  const newLink = new Link({
    url: req.body.url,
    short: short
  });

  newLink.save().then(link => {
    res.json(link);
  });
});

【讨论】:

    【解决方案2】:

    不要混合同步循环和异步条件更新。在DoSomething 调用返回结果之前,可以保证像这样的东西尽可能多地运行while 主体:

    while(cond) {
      // call something async. don't wait for a result.
      DoSomething.asynchronous( () => { cond = false; });
      // then immediately restart the iteration
    }
    

    所以不要那样做。让你的makeShort 异步生成一个短字符串。

    const symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    const symbolCount = symbols.length;
    
    function makeShort(howMany) {
      howMany = howMany || 5;
      let short = "";
      while(howMany--) {
        short += symbols[(Math.random() * symbolCount)|0];
      }
      return short;
    }
    

    然后,独立进行验证:

    function assignShortForm(req, res) {
      let short = makeShort();
    
      verifyShortIsAvailable(
        short,
        success => {
          // this short form was available
          new Link({ url: req.body.url, short }).save().then(link => res.json(link));
        }, error => {
          // try again. RNG is not your friend, and this COULD run a very long time.
          assignShortForm(req, res);          
        }
      );
    }
    

    您的路由器使用该功能,而不是内联它:

    router.post('/', assignShortForm);
    

    在此,verifyShortIsAvailable 应该异步完成它的工作:

    verify verifyShortIsAvailable(short, resolve, reject) {
      Link.findOne(query, (err, link) => {
        if (err) return reject(err);
        if (link) return reject("...");
        return resolve();
      });
    }
    

    【讨论】:

      【解决方案3】:

      while 循环同步运行,这意味着它们会阻止线程进一步执行,直到它们完成。因为链接缩短器是异步的,所以它被while 循环阻塞。

      要异步处理此代码,您可以返回Promise

      function makeShort() {
      
          var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      
          // note length was previously undefined in the comparison. use possible.length or another arbitrary value
          for(var i = 0; i < possible.length; i++){
              short += possible.charAt(Math.floor(Math.random() * possible.length));
          }
      
          let query = {short:short};
          return new Promise((resolve, reject) => {
              Link.findOne(query, (err, link) => {
                  if(err) return reject(err);
                  resolve(link)
              });
          })
      }
      

      那你就可以这样使用了……

      let short = makeShort().then(shortLink => {
          // do something with the link
      }).catch(err => {
          // handle the error
      });
      

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-15
        • 2018-08-25
        • 2021-01-19
        • 1970-01-01
        • 1970-01-01
        • 2014-08-05
        • 2012-12-24
        • 1970-01-01
        相关资源
        最近更新 更多