基于rail标准,通过webworker,长任务拆分,分帧执行,让出主线程,利用浏览器空闲执行等方式
避免js线程长期执行,阻塞UI渲染。以达到性能优化的目的。
new-normal-1080.mov

new-opti-1080.mov

npm install dynamic-tasks || yarn add dynamic-tasks 默认ES6语法 使用方自行转译和polyfill 例如vue: 通过配置vue.config.js里面的transpileDependencies 例如react通过配置 rules: [{test: /\.js$/, exclude: /node_modules\/(?!(dynamic-tasks)\/).*/, use:{loader: 'babel-loader', options:{presets: ['@babel/preset-env', '@babel/preset-react'] } } } ] const p1 = (res) =>{return 1111}; const p2 = (res) =>{return 2222}; const p3 = (res) => new Promise((resolve) =>{setTimeout(() =>{resolve(33333)}, 2000)}); const p4 = (res) => new Promise((resolve, reject) =>{setTimeout(() =>{reject(4444)}, 6000)}); const p5 = (res) => new Promise((resolve) =>{setTimeout(() =>{resolve(5555)}, 1000)}); 让出主线程 避免UI卡顿
此方法后面的任务将在下一帧继续执行。
import{yieldToMain} from "dynamic-tasks"; p1() await yieldToMain(); // 中断当前帧 让出给main thread 下一帧继续执行 p2(); 有UI操作并且优先级较高 建议使用DynamicTasks的方式 避免卡顿使用frame参数分帧运行
- 支持动态添加
- 支持并行&串行
- 支持同步&异步
- 支持时间切片
import{DynamicTasks} from "dynamic-tasks"; const task = new DynamicTasks({parallelMax: 3, frame: true }); task.start([{key: "p1", task: p1, parallel: true, },{key: "p2", task: p2, parallel: true, }, ]); task.start([{key: "p3", task: p3, parallel: true, },{key: "p4", task: p4, parallel: true, },{key: "p5", task: p5, // 默认的串行的 会等待前面的全部执行完成 并且可以获取前面的结果 },{key: "p6", task: (allResult)=>{console.log('test allResult', allResult) }, }, ]); 无UI操作 大量运算 建议使用pool的线程池方式运行。
run in web worker thread pool。
独立main thread上下文 使用new Function转换运行,因此不能访问外部变量。
可以通过串行的方式(默认就是串行),获取到上一个task的结果。
可以通过网络获取数据运算。
import{pool } from "dynamic-tasks" const p1 = (res) =>{console.log("test p1 res", res, 1111); return 1111}; const p2 = (res) =>{console.log("test p2 res", 22222)}; const p3 = (res) => new Promise((resolve) =>{const test = () =>{let count = 0; for (let i = 0; i < 1000000000; i++){count = count + i} }; test(); setTimeout(() =>{resolve(33333)}, 5000); console.log("test p3 res", res, 3333333)}); pool([{key: "p1", task: p1, },{key: "p2", task: p2, },{key: "p3", task: p3, }, ], 2).then((res) =>{console.log("test pool:", res);{p1:{data: 1111, key: "p1", status: "succ", }, p2:{data: undefined, key: "p2", status: "succ", }, p3:{data: 33333, key: "p3", status: "succ", } } }); import{clearPool } from "dynamic-tasks" clearPool() 浏览器空闲执行 不紧急的任务建议使用这个api
此时已经渲染完成,UI变更会导致页面重绘应尽量避免
参考react fiber思路通过raf+messagechannel 对不支持requestidlecallback的浏览器做了polyfill。
import{idleCallback } from "dynamic-tasks" idleCallback((params)=>{console.log('test idleCallback params', params) },{timeout: 100}) 浏览器空闲执行 不紧急的任务建议使用这个api 此时已经渲染完成,UI变更会导致页面重绘应尽量避免 内部使用idleCallback方法。
import{idle } from "dynamic-tasks" idle([{key: 'p1',task: p1}],100).then(res =>{console.log('test idle:', res) }) import{serialTask} from "dynamic-tasks"; serialTask([p1,p2,p3]).then(res=>{console.log("test res", res) }) res: [{status: 'succ', data: 1111 },{status: 'succ', data: 2222 },{status: 'succ', data: 33333 } ] import{parallelMaxTask} from "dynamic-tasks"; parallelMaxTask([p1,p2, p3], 2).then((res)=>{console.log('test parallelMaxTask: ', res) }) res: [{status: 'succ', data: 1111 },{status: 'succ', data: 2222 },{status: 'succ', data: 33333 } ] import{TaskCancelable} from "dynamic-tasks const p3 = () => new Promise((resolve) =>{setTimeout(() =>{resolve(33333)}, 2000)}); const cancelP = TaskCancelable(p3()); cancelP .then((res) =>{console.log("test res", res)}) .catch(() =>{if (cancelP.isCancel()){// true console.log("test cancel")} }) .finally(() =>{console.log("test finally isCancel:", cancelP.isCancel()); // true }); cancelP.cancel(); 