- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有以下代码
var data = [
{ id: "0" },
{
id: "1",
children: [
{
id: "1.1",
children: [
{
id: "1.1.1",
children: [
{
id: "1.1.1.1",
children: [
{ id: "1.1.1.1.1" },
{ id: "1.1.1.1.2" },
{ id: "1.1.1.1.3" }
]
},
{ id: "1.1.1.2" },
{ id: "1.1.1.3" }
]
},
{ id: "1.1.2" },
{ id: "1.1.3" },
]
},
{ id: "1.2" },
{ id: "1.3" }
]
},
{ id: "2" },
{ id: "3" }
];
function recursive(current) {
var first = current[0];
current.shift();
var remaining = current;
console.log(first.id);
if (first.children) {
setTimeout(function(){
recursive(first.children);
})
}
if (remaining.length) {
setTimeout(function(){
recursive (remaining);
});
}
}
recursive(data);
最佳答案
缠线
递归和异步是独立的概念,但我们没有理由不能一起使用它们。首先,我们将查看一些同步遍历,然后添加对异步的支持。我喜欢这种回答方式,因为我们可以看到以多种方式表示的同一个程序。我们专注于产生巨大影响的小变化。
我们从一种使用生成器的方法开始——
const Empty =
Symbol ()
const breadthFirst = function* ([ node = Empty, ...nodes ])
{
if (node === Empty)
return
else
(yield node, yield* breadthFirst (nodes.concat (node.children || [])))
}
const data =
[{ id: "0" },{id: "1",children: [{id: "1.1",children: [{id: "1.1.1",children: [{id: "1.1.1.1",children: [{ id: "1.1.1.1.1" },{ id: "1.1.1.1.2" },{ id: "1.1.1.1.3" }]},{ id: "1.1.1.2" },{ id: "1.1.1.3" }]},{ id: "1.1.2" },{ id: "1.1.3" },]},{ id: "1.2" },{ id: "1.3" }]},{ id: "2" },{ id: "3" }]
for (const x of breadthFirst (data))
console.log (x.id)
// 0 1 2 3 1.1 1.2 1.3 1.1.1 ... 1.1.1.1.3
id
数组中的字段
const values =
Array.from (breadthFirst (data), x => x.id)
console.log (values)
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
breadthFirst
一个高阶函数,很像
Array.prototype.map
或
Array.prototype.reduce
const Empty =
Symbol ()
const breadthFirst = (f = identity, [ node = Empty, ...nodes]) =>
node === Empty
? []
: [ f (node), ...breadthFirst (f, nodes.concat (node.children || [])) ]
const data =
[{ id: "0" },{id: "1",children: [{id: "1.1",children: [{id: "1.1.1",children: [{id: "1.1.1.1",children: [{ id: "1.1.1.1.1" },{ id: "1.1.1.1.2" },{ id: "1.1.1.1.3" }]},{ id: "1.1.1.2" },{ id: "1.1.1.3" }]},{ id: "1.1.2" },{ id: "1.1.3" },]},{ id: "1.2" },{ id: "1.3" }]},{ id: "2" },{ id: "3" }]
const values =
breadthFirst (x => x.id, data)
console.log (values)
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
breadthFirst
使用 Promise 的异步函数
const breadthFirst = (f = identity, [ node = Empty, ...nodes]) =>
node === Empty
? Promise.resolve ([])
: breadthFirst (f, nodes.concat (node.children || [])) .then (answer => [ f (node), ...answer ])
const promiseOfValues =
breadthFirst (x => x.id, data)
promiseOfValues.then (console.log, console.error)
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
f
也是异步的
const breadthFirst = (f = identity, [ node = Empty, ...nodes]) =>
node === Empty
? Promise.resolve ([])
: Promise.resolve (node) .then (f) .then (value =>
breadthFirst (f, nodes.concat (node.children || [])) .then (answer =>
[ value, ...answer ]))
const promiseOfValues =
breadthFirst (x => new Promise (r => setTimeout (r, 250, x.id)), data)
promiseOfValues.then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
async
/
await
句法
const breadthFirst = async (f = identity, [ node = Empty, ...nodes]) =>
{
if (node === Empty)
return []
const value =
await Promise.resolve (node) .then (f)
const answer =
await breadthFirst (f, nodes.concat (node.children || []))
return [ value, ...answer ]
}
const promiseOfValues =
breadthFirst (x => new Promise (r => setTimeout (r, 250, x.id)), data)
promiseOfValues.then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
breadthFirst
承担许多责任。除了构建正确的节点序列之外,我们还需要考虑 Promise API 以及如何将序列连接在一起;这是一种负担,可以解除。下面,我们可以使用反向折叠使该过程更通用 -
unfold
const unfold = (f, init) =>
f ( (x, next) => [ x, ...unfold (f, next) ]
, () => []
, init
)
const nextLetter = c =>
String.fromCharCode (c.charCodeAt (0) + 1)
const alphabet =
unfold
( (next, done, c) =>
c > 'z'
? done ()
: next (c, nextLetter (c))
, 'a'
)
console.log (alphabet)
// [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z ]
Array.prototype.reduce
获取一组值并将其简化为单个值 -
unfold
获取单个值并将其展开为值的集合
const fib = (n = 0) =>
unfold
( (next, done, [ n, a, b ]) =>
n < 0
? done ()
: next (a, [ n - 1, b, a + b ])
, [ n, 0, 1 ]
)
console.log (fib (20))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ]
async
和
await
关键词
const asyncUnfold = async (f, init) =>
f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
, async () => []
, init
)
getChildren
.在实际程序中,这可能会获取一个节点或节点 ID,并从返回该节点子节点的 Promise 的数据库中获取它。下面,我们看到
breadthFirst
中的复杂性显着降低。 .请注意,这里的 Promise 的复杂性不会给程序员带来负担
const getChildren = (node) =>
new Promise ((resolve, _) =>
setTimeout (resolve, 250, node.children || []))
const Empty =
Symbol ()
const breadthFirst = (nodes) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
: next (node.id, [ ...rest, ...await getChildren (node) ])
, nodes
)
breadthFirst (data) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
unfold
各种遍历的函数——下面我们实现
depthFirst
等同于
breadthFirst
但这一次我们做了一个微小的改变
const breadthFirst = (nodes) =>
const depthFirst = (nodes) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
// breadth-first
next (node.id, [ ...rest, ...await getChildren (node) ])
// depth-first
: next (node.id, [ ...await getChildren (node), ...rest ])
, nodes
)
depthFirst (data) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ '0', '1', '1.1', '1.1.1', '1.1.1.1', '1.1.1.1.1', '1.1.1.1.2', ..., '2', '3' ]
data
的最后评论是许多人在对数据的层次树建模时犯的一个错误。在您的
data
对象,每一项都是一个Node,每一项都是
children
是一个节点;但是,
data
它本身不是一个节点,它只是一个普通的数组。这种不一致是一个痛点,实际上使您的程序不那么通用。
reduce
) 和
unfold
多于?
reduce
接受一个集合并产生一个值,
unfold
相反。遍历树时,我们从单个节点开始,而不是节点数组。
const breadthFirst = (nodes) =>
const breadthFirst = (node) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
: next (node.id, [ ...rest, ...await getChildren (node) ])
, nodes
, [ node ]
)
const depthFirst = (nodes) =>
const depthFirst = (node) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
: next (node.id, [ ...await getChildren (node), ...rest ])
, nodes
, [ node ]
)
breadthFirst ({ id: 'root', children: data }) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ 'root', '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
depthFirst ({ id: 'root', children: data }) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ 'root', '0', '1', '1.1', '1.1.1', '1.1.1.1', '1.1.1.1.1', '1.1.1.1.2', ..., '2', '3' ]
const asyncUnfold = async (f, init) =>
f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
, async () => []
, init
)
const Empty =
Symbol ()
const depthFirst = (node) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
: next (node.id, [ ...await getChildren (node), ...rest ])
, [ node ]
)
const breadthFirst = (node) =>
asyncUnfold
( async (next, done, [ node = Empty, ...rest ]) =>
node === Empty
? done ()
: next (node.id, [...rest, ...await getChildren (node) ])
, [ node ]
)
const getChildren = (node) =>
new Promise ((resolve, _) =>
setTimeout (resolve, 250, node.children || []))
const data =
[{ id: "0" },{id: "1",children: [{id: "1.1",children: [{id: "1.1.1",children: [{id: "1.1.1.1",children: [{ id: "1.1.1.1.1" },{ id: "1.1.1.1.2" },{ id: "1.1.1.1.3" }]},{ id: "1.1.1.2" },{ id: "1.1.1.3" }]},{ id: "1.1.2" },{ id: "1.1.3" },]},{ id: "1.2" },{ id: "1.3" }]},{ id: "2" },{ id: "3" }]
breadthFirst ({ id: 'foo', children: data }) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ 'foo', '0', '1', '2', '3', '1.1', '1.2', ... '1.1.1.1.3' ]
depthFirst ({ id: 'bar', children: data }) .then (console.log, console.error)
// => Promise
// 4 seconds later ...
// [ 'bar', '0', '1', '1.1', '1.1.1', '1.1.1.1', '1.1.1.1.1', '1.1.1.1.2', ..., '2', '3' ]
关于javascript - 带有 setTimeout 的递归 JS 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49523620/
要求:用户连续扫描文本框中的作业编号,没有任何延迟。对于每个职位编号,我需要在后台调用 API 来获取扫描职位编号的详细信息。 我做了什么:我编写了一个小模拟代码来激发这个需求。我使用 setTime
我遇到了一个问题:该代码应该按该顺序输出“hi1”“hi2”“hi3”“hi4”。我写了这个简化的代码,实际代码更复杂,导致我无法删除我标记的一些功能。 function test() { c
我的页面上有一个动态创建的 iframe。像这样: var iframe = document.createElement('iframe'); iframe.setAttribute("id","m
我确信这是一个被问过很多次的通用问题,但找不到解决方案。 我有 javascript 使用 setTimeout() 函数来关闭我在设定时间后创建的弹出窗口。 问题:如果我在与创建弹出窗口的脚本相同的
我想在第一个函数完成后执行第二个函数。 结果: i: 0,i: 1,...,i: 9, j: 0,j: 1,...,j: 9 function first(callback){ for
我正在尝试创建一个按钮,以反馈其正在执行的操作。在 Angular 中,我向服务器发出一个放置请求——此时我更改按钮的状态以指示这一点——然后当我收到响应时,我再次更改按钮的状态以反射(reflect
我正在尝试制作一个字符串,它会逐个字母地写出自己直到完成句子,并且每个字母出现的速度基于从 1 到 10 不等的输入。在字符串的末尾,它会闪烁5 秒,直到外星人出现。我的想法是创建一个 setInte
在 Meteor 中,为什么要使用 Meteor.setTimeout() 而不是普通的 setTimeout()? 使用 Meteor.setTimeout() 而不是单纯的 setTimeout
我有这个代码 - function example() { var i = 0; function add() { i++; } setTimeout(
我想知道它们之间有什么区别 window.setTimeout(myFancyFunciton, 1000); 和 setTimeout(myFancyFunciton, 1000); 两者似乎都在做
好吧,我好像遇到了问题。我正在尝试创建一个twicker 来显示数据行。我正在使用 jquery/javascript 在一定时间后隐藏和显示行。代码如下: var timer_is_on
编辑:我最终想在以后使用 setTimeout 恢复变量的先前值 我创建了以下示例来说明我的观点:( JSFiddle ) Push the button Try it var x = {};
我一直在像这样在没有窗口父级的情况下使用超时: setTimeout(FUNC, 1000); 我很好奇,我应该这样使用它吗? window.setTimeout(FUNC, 1000); 有区别吗?
我有一个使用 setTimeout 函数执行动画的函数,结构如下: animation: function() { //first setTimeout(function(){ mak
关闭。这个问题是not reproducible or was caused by typos .它目前不接受答案。 这个问题是由于错别字或无法再重现的问题引起的。虽然类似的问题可能是on-topi
这是一个快速的(损坏的)jsfiddle:http://jsfiddle.net/wH2qF/ 由于某种原因这不起作用...是因为我在另一个 setTimeout 的处理程序中有一个 setTimeo
我有两个 setTimouts,如下所示,根据 if 条件,我想跳过一个超时。 var batchID = []; batchID = getBatchIDs();//this function ge
我只看到一种情况我应该使用 window.setTimeout 而不是 setTimeout,当我在我的闭包,这显然不是很好的做法(除非有非常特殊的用途)。 我注意到 Google Closure 编
我看到这个用了很多,有人告诉我把函数引用放在引号之间是不好的,因为 setTimeout/setInterval evals 引用。这两者之间的实际区别是什么,以至于一个被使用在另一个之上?为什么我看
我正在使用“setTimeout”函数。此代码按预期运行: function myFunction() { console.log('test'); setTimeout(myFunc
我是一名优秀的程序员,十分优秀!