Javascript 如何给一个深度递归(使用了setTimeout)设置完成后回调?

2012-09-28 15:15:07 +08:00
 yulanggong
因为递归太深而使用了异步的 setTimeout 清空调用栈,整个递归变成了异步的行为,怎么设置完成后的回调?

简单的代码示例:

var stackSize = 0;

function foo(a){
stackSize ++;

bar();

if (!a.length) return;

if (stackSize > 1000) {
stackSize = 0;
setTimeout(function(){
a.forEach(foo);
},0);
} else {
a.forEach(foo);
}
}

foo(hugeArray); //hugeArray 是一个多层嵌套的数组, 像这样 [[[...],[...]],[...]]
3947 次点击
所在节点    JavaScript
14 条回复
yulanggong
2012-09-28 15:26:22 +08:00
怎么不能编辑了?

这是上面的代码
<script src="https://gist.github.com/3798444.js"> </script>
yulanggong
2012-09-28 15:27:36 +08:00
yulanggong
2012-09-28 15:30:39 +08:00
haohaolee
2012-09-28 15:36:17 +08:00
一个疑问,stackSize 真的能反映当前递归的深度吗?出函数的时候是不是要 stackSize-- 一下啊?
另外,为什么要选择这么难以理解的解决方式呢,如果不是非要使用递归的话,可以用一个数据结构模拟栈或者别的神马嘛,比如树的遍历
skyleft
2012-09-28 15:39:31 +08:00
使用全局变量
yulanggong
2012-09-28 15:46:49 +08:00
@haohaolee
stackSize 的确是错的,因为 forEach 那会进入不同的分支。可以改为 stackSize 作为参数传给递归的函数用来分别统计。
你能说说用数据结构模拟栈的思路吗?我没什么编程基础。
chone
2012-09-28 15:54:37 +08:00
callback是不是要在所有遍历完成后调用,那样需要能逐级向上反馈完成的信号吧,这样在子级遍及没有完成前,父级需要被阻塞以等待信号。阻塞的实现好像也要用settimeout,父级和子级之间的通讯可以使用forEach的bind参数。

不过这样的处理又复杂,效率又差。@haohaolee 说的比较靠谱。
yulanggong
2012-09-28 17:02:57 +08:00
@chone
我搜了下非递归遍历,发现这可能是 @haohaolee 说得方式,我在琢磨怎么写代码。

逐级汇报是一个思路,不过我还想不出怎么实现。

我原来有个思路是在递归的函数里累加一个全局变量,然后定时轮询这个变量,变量在一段时间内没有变化时就是递归完成了,只是这样回调不太及时。
haohaolee
2012-09-28 17:20:17 +08:00
@yulanggong 我觉得你可以参考树遍历的方式,应该有很多资料。
提供一个思路,设想有一个队列 q (javascript原生是否支持队列我不知道)
1. 把a数组放进队列 2 若队列不为空循环处理队列: 取出队列头的元素,遍历该数组,若元素仍为数组则加入队列尾,若为非数组则直接处理
skyleft
2012-09-28 17:32:38 +08:00
使用栈实现递归,类似树的非递归遍历,但是树的节点之间有指针链接,这里用两个栈。

初始化两个栈S1,S2(js无,自己实现),s1存下标,s2存数组
s1.push(0)
s2.push(arr)
while(s not empty){
i=s1.pop();arri=s2.pop();
循环arri,如果是元素,就访问它,如果还是数组,就将其在父数组中的下标压入栈S1,将父数组压入栈S2
}
shawiz
2012-09-28 17:41:35 +08:00
non-blocking 的 callback 可以用 jquery 的 deferred 来解决。
比如:

<script src="https://gist.github.com/3798898.js"> </script>
shawiz
2012-09-28 17:42:13 +08:00
haohaolee
2012-09-28 18:51:41 +08:00
@shawiz 没看懂这个例子,这个例子能做到所有的foo都结束的时候回调吗
shawiz
2012-09-29 14:43:23 +08:00
@haohaolee 豆瓣上别人推荐了一篇关于 Javascript 异步的文章,值得一读,也许能解决你的疑问。我介绍的是 promise 方法。

http://jimliu.net/?p=12

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/48930

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX