簡體   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