递归终止:return语句与布尔变量的区别及API调用疑问
嘿,作为刚接触JavaScript递归的新手,你遇到的这两个问题其实是异步编程和递归逻辑结合时的典型坑,咱们一步步拆解清楚:
一、为什么return方式会导致无限循环?
先看你第一个代码片段的核心问题:
async function foo(index) { let var2 = await fetch(...) .then(blob => blob.json()) .then(data => { if(data.id == 6) { return; // 这个return只终止当前的then回调,不是foo函数! } }) foo(++index); // 不管then里发生什么,这行都会执行! }
你的认知误区在这里:then回调函数是独立的执行上下文,里面的return只能终止当前回调,无法跳出外层的foo函数。当data.id == 6时,你在第二个then里return,只是结束了这个回调的执行,但外层的foo函数并不会因此停止。await等待的是整个fetch promise链完成,但不管promise链内部做了什么,foo函数在await之后的foo(++index)都会被执行,所以递归会一直触发,导致无限循环。
而布尔变量的方式之所以有效,是因为你把递归的触发条件放在了foo函数的主流程里:
if(bool) { foo(++index); }
当data.id == 6时,你把bool设为false,等promise链完成后,foo函数会检查这个变量,发现为false就不再发起下一次递归,自然就终止了。
二、为什么去掉await会导致布尔变量方式也无限循环?
fetch是异步操作,返回的是一个Promise。当你去掉await后,foo函数不会等待API请求完成,会立即执行后面的foo(++index)。也就是说,在API返回数据、修改bool变量之前,递归已经疯狂触发了N次,bool一直是初始值true,根本没机会被修改为false,所以递归会无限进行下去。
调用API时必须用await吗?
不是所有场景都必须,但在你的递归逻辑里是绝对必要的。await的作用是让async函数暂停执行,直到Promise状态变为resolved(也就是API返回数据),这样你才能在拿到数据后,正确判断是否要继续递归。如果不用await,异步操作的结果会在后续的事件循环里处理,但递归调用已经同步触发了,逻辑完全错乱,自然达不到预期效果。
优化小建议
其实你可以不用布尔变量,直接在foo函数里控制递归,让逻辑更简洁:
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; // 这次return是终止foo函数本身,有效! } // 没到终止条件才继续递归 await foo(++index); } catch(err) { console.log(err); } }
这里把await直接用在fetch和json解析上,避免嵌套then回调,当data.id等于6时,return直接终止当前的foo函数,也就不会触发下一次递归了,逻辑更清晰。
内容的提问来源于stack exchange,提问作者DJG

