【问题标题】:Recursion : return vs boolean exit递归:返回与布尔退出
【发布时间】:2020-06-29 10:19:59
【问题描述】:

我对 JavaScript 很陌生,我一直在使用这个 api,我基本上只是在测试这些递归调用是如何工作的:如你所见,我试图在点击数据后停止调用。 id == 6。

使用“return”退出递归调用与使用布尔值有什么区别? 我认为他们会产生相同的输出;但是,他们没有。布尔值产生预期的结果,而“返回”产生无限循环。我以为返回将是最后一个电话。我的思维过程中的漏洞在哪里?

另外,请注意,在调用 api 调用时是否应该始终使用 await?当我取消等待时,它产生了一个无限循环,即使使用布尔方法也是如此。这是为什么呢?

感谢您的宝贵时间。

    let bool = true;
    async function foo(index) {

        let var2 = await fetch(`https://jsonplaceholder.typicode.com/todos/${index}`)
        .then(blob => blob.json())
        .then(data => {
            if(data.id == 6) {
                return;
            }
        })
        .catch(err => console.log(err));


        foo(++index);
}
let bool = true;
async function foo(index) {

    let var2 = await fetch(`https://jsonplaceholder.typicode.com/todos/${index}`)
    .then(blob => blob.json())
    .then(data => {
        if(data.id == 6) {
            bool = false;
        }
        console.log(data);
    })
    .catch(err => console.log(err));

    if(bool) {
        foo(++index);
    }
}

【问题讨论】:

  • 根据我的经验,return 是一种打破递归的更易读的方式,并且由于您使用的是异步等待,它可以被简化并像同步代码一样阅读

标签: javascript recursion promise fetch


【解决方案1】:

您将async/awaitPromise 混合在一起,但实际上这是同一事物的两个版本。

假设您想从远程服务中获得某些东西,在这种情况下是您的fetch('https... - 您实际上是在发送一个请求,然后想要在完成后做其他事情。这曾经是通过 回调 来完成的:

// pseudocode, not actual JS
oldFetch('https...', result => {
    // This code fires when the fetch finishes
    someOtherAsyncRequest(..., secondREesult => {
        // This fires when the second async action is done
    });
});

// Code here fires immediately, it doesn't wait for callbacks

您很快就会遇到回调嵌套在回调中并且错误跟踪一团糟的问题。解决方案是 Promise - Promise 在成功时将调用 then 或在失败时调用 catch 而不是回调函数。只要下一个函数也返回Promise,这让您链接这些回调请求

fetch(`https...`)                     // Returns a promise with the response
    .then(blob => blob.json())        // Returns a promise when the JSON is finished parsing
    .then(data => {                   // Doesn't return anything, so chain stops
        if(data.id == 6) {
            bool = false;
        }
        console.log(data);
    })
    .catch(err => console.log(err));  // Catch exceptions

请注意,这些then/catch 语句都不会停止代码执行,它们实际上只是将回调包装在一个对象中,以便您更好地管理它们。

async/await 只是使Promise 易于使用的语法,您仍然拥有Promise 并且可以在它们之间切换,但是async/await 更容易阅读。

async这个函数返回一个Promise(但它不是必须的)。

await 表示将所有代码放在 then(... 中。

把它们放在一起,代码的可读性就更高了:

// Does exactly the same as the previous code example
try {
    const blob = await fetch(`https...`);
    const data = await blob.json();
    if(data.id == 6) {
        bool = false;
    }
    console.log(data);
}
catch(err) {  // Catch exceptions - if using await anything async can be in a regular try-catch
   console.log(err);
}

让我们将它应用到您的函数中。它仅适用于 await 的原因是,当它不存在时,整个函数在调用 then 代码之前完成,但函数的其余部分在另一个 then 中。

您正在混合语法,所以我们只使用async/await

async function foo(index) { // async means this function can return a Promise
    try {
        const blob = await fetch(`https://jsonplaceholder.typicode.com/todos/${index}`);
        const data = await blob.json();

        if(data.id == 6)
            return; // await means this will exit the parent function
       
        console.log(data);
    }
    catch(err) {
        console.log(err);
    }

    await foo(++index);
}

使用“return”退出递归调用与使用布尔值有什么区别?

您只是从回调返回,您需要通过解析Promise 来处理它或只是等待它。

当我取消等待时,它产生了一个无限循环,即使使用布尔方法也是如此。这是为什么呢?

你的let bool = true; 是全局的,如果你不await fetch(... 下一行立即执行,调用foo。 JS 会在检查fetch 的结果之前完成同步代码,因此在任何fetch 结果更改bool = false 之前,您将用完递归。

在调用 api 调用时是否应该始终使用 await?

如果 API 是 async 或基于 Web 的,则可以。作为一般规则,一旦某些代码是async,所有这些都必须是 - 您不能(或不应该)使用同步代码调用异步方法。最佳实践是使用async/await任何地方并为其设计。

【讨论】:

  • 非常感谢您的详细回复。这很有帮助!
【解决方案2】:

你在 Promise 中返回。所以 Promise 将被返回,这意味着,在你的情况下, var2 将是 return 的值(这是未定义的)。但是,您再次调用 foo 没有任何限制,所以它总是在第一种情况下被调用。在您给出的第二个示例中,您向递归调用添加了一个约束,这就是它不再被调用的原因。

【讨论】:

    【解决方案3】:

    函数中间的这一点:

    data => {
      if (data.id == 6) {
        return;
      }
    }
    

    是一个函数。它里面的返回只从这个函数返回。因此,如果 data.id 为 6,则返回。如果没有,我们到达这个函数的结尾——也返回。因此这里的返回完全没有效果

    【讨论】:

      【解决方案4】:

      首先,您应该了解有关您的代码的几件事:

      1. 这不是发出多个async 请求的正确方法。
      2. 您将async-await 语法与promise-chaining 混合使用。

      在第一个代码示例中,在then 块内使用return 停止递归会导致无限循环,因为then 块内的return 仅从传递给then 函数的回调函数返回.它不会从外部函数返回。

      在第二个代码示例中,当bool 变量的值设置为false 时,函数停止递归调用自身。

      要正确发出多个请求,请使用Promise.all() 函数。这是一个example,它展示了如何使用Promise.all() 函数发出多个请求。

      在调用 api 调用时是否应该始终使用 await?

      async 函数内部,是的,你不能在async 函数外部使用await 关键字

      当我关闭等待时,它产生了一个无限循环,即使使用 布尔方法。这是为什么呢?

      在这种情况下,您在再次调用函数之前没有等待请​​求的结果,这会导致无限递归。

      您可以如下所示更改代码以获得预期结果

      async function foo(index) {
        try {
          const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${index}`);
          const data = await response.json();
      
          if (data.id === 6) {
            return;
          }
      
          foo(++index);
      
        } catch (error) {
          console.log(error.message);
        }
      }
      

      【讨论】:

      • 谢谢。我没有意识到我只是从回调中返回。我感谢详细的回复
      猜你喜欢
      • 1970-01-01
      • 2015-04-12
      • 2018-08-02
      • 1970-01-01
      • 2015-12-27
      • 2015-02-08
      • 2021-01-06
      • 1970-01-01
      • 2013-10-15
      相关资源
      最近更新 更多