60fps JS,同时对数百万条记录进行排序、映射和减少(使用空闲时间协程)
js-协程
更新协程
在这里获取库:
它是如何工作的?
js-协程
我之前在 dev.to 上读到一些非常有趣的东西后,突然灵光一闪- 给了我一个想法 - 而且哇,它真的有效!
我问了自己这个问题:
什么时候在 JavaScript 应用的主线程上对大型数组进行排序才合适?当然,只要你不介意用户看到所有动画和特效卡顿,任何时候都可以。即使转移到工作线程,也会因为序列化而影响主线程,导致一切卡顿。
那么什么时候才是合适的时机呢?嗯,就是在所有动画没有任何动作、系统空闲的间隙里。如果你能写点东西来消耗这段时间,然后把控制权交给系统,让它可以执行动画并完成剩下的工作,然后在下一个间隙继续执行就好了。现在你可以……
现在支持异步 JSON请参阅后续文章!
等等,还有更多!
使用协程的另一个超级实用的方法是制作动画并控制复杂的状态——js-coroutines 也提供了强大的update
方法,可以以高优先级运行每一帧。见下文。
它配备了最有用的数组函数:
- forEach
- 地图
- 筛选
- 减少
- 查找索引
- 寻找
- 一些
- 每一个
- 种类
- 追加(数组到数组)
- concat(将两个数组合并为一个新数组)
这个辅助函数yielding
将一个普通函数包装成一个生成器,并每隔几次迭代检查一次剩余时间。你可以在上图中看到它的用法。但它只是一个辅助函数——如果你的map
函数需要做更多工作,它本身可以是一个生成器,在需要的时候 yield,也可以将结果传递给更深层的、可以 yield 的函数:
const results =
yield *
map(inputArray, function* (element, index) {
//Every 200 indices give up work
//on this frame by yielding 'true'
//yield without true, checks the amount
//of remaining time
if (index % 200 === 199) yield true;
//Yield out a filter operation
let matched = yield* filter(
element,
yielding((c) => c > 1000)
);
//Now yield out the calculation of a sum
return yield* reduce(
matched,
yielding((c, a) => c + a),
0
);
});
yielding(fn, [optional yieldFrequency]) -> function *
更新协程
实现状态动画的一个好方法是使用每帧运行的协程。在这种情况下,当你yield
在下一帧被回调时,状态动画就变得轻而易举了:
import { update } from "js-coroutines";
//Animate using a coroutine for state
update(function* () {
while (true) {
//Move left to right
for (let x = -200; x < 200; x++) {
logoRef.current.style.marginLeft = `${x * multiplier}px`;
yield;
//Now we are on the next frame
}
//Move top to bottom
for (let y = 0; y < 200; y++) {
logoRef.current.style.marginTop = `${y * multiplier}px`;
yield;
}
//Move diagonally back
for (let x = 200; x > -200; x--) {
logoRef.current.style.marginLeft = `${x * multiplier}px`;
logoRef.current.style.marginTop = ((x + 200) * multiplier) / 2 + "px";
yield;
}
}
});
正如您在此性能捕获中所看到的,排序和处理均匀分布在各个帧中,保持 60fps。
在这里获取库:
或者
npm i js-coroutines
执照
js-coroutines - MIT (c) 2020 Mike Talbot