繁体   English   中英

如何在不破坏堆栈的情况下等待一系列异步任务?

[英]How to await an Array of async Tasks without blowing the stack?

如果您想等待所有Task数组,即使数组折叠是堆栈安全的,也会破坏堆栈,因为它会产生一个大的延迟函数调用树:

 const record = (type, o) => (o[type.name || type] = type.name || type, o); const thisify = f => f({}); const arrFold = f => init => xs => { let acc = init; for (let i = 0; i < xs.length; i++) acc = f(acc) (xs[i], i); return acc; }; const Task = task => record( Task, thisify(o => { o.task = (res, rej) => task(x => { o.task = k => k(x); return res(x); }, rej); return o; })); const taskMap = f => tx => Task((res, rej) => tx.task(x => res(f(x)), rej)); const taskOf = x => Task((res, rej) => res(x)); const taskAnd = tx => ty => Task((res, rej) => tx.task(x => ty.task(y => res([x, y]), rej), rej)); const taskAll = arrFold(tx => ty => taskMap(([x, y]) => xs => x => xs.concat([x])) (taskAnd(tx) (ty))) (taskOf([])); const inc = x => Task((res, rej) => setTimeout(x => res(x + 1), 0, x)); const xs = Array(1e5).fill(inc(0)); const main = taskAll(xs); main.task(console.log, console.error);

为了解决这个问题,你通常使用特殊的数据结构和相应的蹦床来中断函数调用:

const Call = f => (...args) =>
  ({tag: "Call", f, args});

const deferredRec = step => {
  while (step && step.tag === "Call")
    step = step.f(...step.args);

  return step;
};

现在taskAll的决定性函数似乎是taskMap ,其中两个操作结束了堆栈:

const taskMap = f => tx =>
  Task((res, rej) =>
    tx.task(x => res(f(x)), rej));
//               ^^^^^^^^^
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

const taskMap = f => tx =>
  Task((res, rej) =>
    Call(f => tx.task(f)) (x => Call(res) (f(x)), rej));

虽然调整防止了堆栈溢出,但不幸的是它阻止了计算从运行到完成,也就是说最终的 continuation console.log永远不会被调用,但在调用inc一次后计算停止(见A行):

 const deferredRec = step => { while (step && step.tag === "Call") step = step.f(...step.args); return step; }; const Call = f => (...args) => ({tag: "Call", f, args}); const record = (type, o) => (o[type.name || type] = type.name || type, o); const thisify = f => f({}); const arrFold = f => init => xs => { let acc = init; for (let i = 0; i < xs.length; i++) acc = f(acc) (xs[i], i); return acc; }; const Task = task => record( Task, thisify(o => { o.task = (res, rej) => task(x => { o.task = k => k(x); return res(x); }, rej); return o; })); const taskMap = f => tx => Task((res, rej) => Call(f => tx.task(f)) (x => Call(res) (f(x)), rej)); const taskOf = x => Task((res, rej) => res(x)); const taskAnd = tx => ty => Task((res, rej) => tx.task(x => ty.task(y => res([x, y]), rej), rej)); const taskAll = arrFold(tx => ty => taskMap(([xs, x]) => xs.concat([x])) (taskAnd(tx) (ty))) (taskOf([])); const inc = x => Task((res, rej) => setTimeout(x => (console.log("inc"), res(x + 1)), 0, x)); // A const xs = Array(3).fill(inc(0)); const main = taskAll(xs); deferredRec(main.task(console.log, console.error));

这怎么能做对呢? 对于各种 CPS 代码,是否有更通用的方法? 请注意,我不想放弃懒惰。

我自己想通了。 inc (line A ) 只需要应用trampoline:

 const deferredRec = step => { while (step && step.tag === "Call") step = step.f(...step.args); return step; }; const Call = f => (...args) => ({tag: "Call", f, args}); const record = (type, o) => (o[type.name || type] = type.name || type, o); const thisify = f => f({}); const arrFold = f => init => xs => { let acc = init; for (let i = 0; i < xs.length; i++) acc = f(acc) (xs[i], i); return acc; }; const Task = task => record( Task, thisify(o => { o.task = (res, rej) => task(x => { o.task = k => k(x); return res(x); }, rej); return o; })); const taskMap = f => tx => Task((res, rej) => Call(f => tx.task(f)) (x => Call(res) (f(x)), rej)); const taskOf = x => Task((res, rej) => res(x)); const taskAnd = tx => ty => Task((res, rej) => tx.task(x => ty.task(y => res([x, y]), rej), rej)); const taskAll = arrFold(tx => ty => taskMap(([xs, x]) => xs.concat([x])) (taskAnd(tx) (ty))) (taskOf([])); const inc = x => Task((res, rej) => setTimeout(x => deferredRec(res(x + 1)), 0, x)); // A const xs = Array(1e4).fill(inc(0)); const main = taskAll(xs); deferredRec(main.task(console.log, console.error));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM