Task
是一种monadic类型,表示顺序执行的异步计算。与Promise
一样,有一个类似于Promise.all
的组合器,但它的运行顺序如前所述:
const TYPE = Symbol.toStringTag;
const struct = type => cons => {
const f = x => ({
["run" + type]: x,
[TYPE]: type,
});
return cons(f);
};
const Task = struct("Task") (Task => k => Task((res, rej) => k(res, rej)));
const arrFold = alg => zero => xs => {
let acc = zero;
for (let i = 0; i < xs.length; i++)
acc = alg(acc) (xs[i]);
return acc;
};
const tMap = f => tg =>
Task((res, rej) => tg.runTask(x => res(f(x)), rej));
const tOf = x => Task((res, rej) => res(x));
const delay = (ms, x) =>
Task(f => setTimeout(f, ms, x), f => f(x));
const tAnd = tf => tg =>
Task((res, rej) =>
tf.runTask(f =>
tg.runTask(g =>
res([f, g]), rej),
rej));
const tAll =
arrFold(acc => tf =>
tMap(([xs, x]) =>
(xs.push(x), xs)) // A
(tAnd(acc) (tf)))
(tOf([]));
const main = tAll([
delay(200, 'a'),
delay(500, 'b'),
delay(100, 'c')]);
const main2 = tAll([
delay(100, 'd')]);
main.runTask(console.log, console.error); // ["d"]
main2.runTask(console.log, console.error); // ["d", "a", "b", "c"]
第A
行中的突变显然会引起副作用。我可以通过用Array.prototype.concat
代替破坏性推送来避免这种情况。
但是,concat
效率低下。说我必须编辑1,000,000个文件。好吧,您可以说这会很慢,因为每个文件都是按顺序处理的。但是我敢打赌,这个问题也会在其他情况下出现。
有没有办法将这种突变转化为局部的,不可观察的突变?
顺便说一句,我知道持久性数据结构将使我能够更有效地使用concat
,但我想在Javascript中避免使用它们。
答案 0 :(得分:2)
您已将arrFold
定义为咖喱函数。然后,您可以使用它来定义tAll
,方法是将其传递给三个必需参数中的两个:
const arrFold = alg => zero => xs => { /* ... */ }
const tAll = arrFold
(acc => tf => tMap(([xs, x]) => (xs.push(x), xs)) (tAnd(acc) (tf)))
(tOf([]));
在这里,您基本上将数组实例烘烤到tAll
函数中,以便在您使用它折叠任务数组时用作zero
。
我可以想到的两种解决方案是(1)要么使arrFold
使用“惰性” zero
参数:
const arrFold = alg => zero => xs => {
let acc = zero();
/* ... */
}
const tAll = arrFold
(/* ... */)
(() => tOf([]))
const TYPE = Symbol.toStringTag;
const struct = type => cons => {
const f = x => ({
["run" + type]: x,
[TYPE]: type,
});
return cons(f);
};
const Task = struct("Task") (Task => k => Task((res, rej) => k(res, rej)));
const arrFold = alg => zero => xs => {
let acc = zero();
for (let i = 0; i < xs.length; i++)
acc = alg(acc) (xs[i]);
return acc;
};
const tMap = f => tg =>
Task((res, rej) => tg.runTask(x => res(f(x)), rej));
const tOf = x => Task((res, rej) => res(x));
const delay = (ms, x) =>
Task(f => setTimeout(f, ms, x), f => f(x));
const tAnd = tf => tg =>
Task((res, rej) =>
tf.runTask(f =>
tg.runTask(g =>
res([f, g]), rej),
rej));
const tAll =
arrFold(acc => tf =>
tMap(([xs, x]) =>
(xs.push(x), xs)) // A
(tAnd(acc) (tf)))
(() => tOf([]));
const main = tAll([
delay(200, 'a'),
delay(500, 'b'),
delay(100, 'c')]);
const main2 = tAll([
delay(100, 'd')]);
main.runTask(console.log, console.error); // ["d"]
main2.runTask(console.log, console.error); // ["d", "a", "b", "c"]
或者,(2),每当您调用tAll
时都要创建一个新的zero
自变量:
const tAll = tasks => arrFold
(/* ... */)
(tOf([]))
(tasks)
const TYPE = Symbol.toStringTag;
const struct = type => cons => {
const f = x => ({
["run" + type]: x,
[TYPE]: type,
});
return cons(f);
};
const Task = struct("Task") (Task => k => Task((res, rej) => k(res, rej)));
const arrFold = alg => zero => xs => {
let acc = zero;
for (let i = 0; i < xs.length; i++)
acc = alg(acc) (xs[i]);
return acc;
};
const tMap = f => tg =>
Task((res, rej) => tg.runTask(x => res(f(x)), rej));
const tOf = x => Task((res, rej) => res(x));
const delay = (ms, x) =>
Task(f => setTimeout(f, ms, x), f => f(x));
const tAnd = tf => tg =>
Task((res, rej) =>
tf.runTask(f =>
tg.runTask(g =>
res([f, g]), rej),
rej));
const tAll = tasks =>
arrFold(acc => tf =>
tMap(([xs, x]) =>
(xs.push(x), xs)) // A
(tAnd(acc) (tf)))
(tOf([]))
(tasks);
const main = tAll([
delay(200, 'a'),
delay(500, 'b'),
delay(100, 'c')]);
const main2 = tAll([
delay(100, 'd')]);
main.runTask(console.log, console.error); // ["d"]
main2.runTask(console.log, console.error); // ["d", "a", "b", "c"]