dynamic-tasks
v1.2.1
Published
基于promise的高性能任务库
Downloads
62
Maintainers
Readme
一个避免卡顿的js任务库
核心思路:
基于rail标准,通过webworker并行,长任务拆分&分帧执行,让出主线程,利用浏览器空闲执行等方式 避免js线程长期执行,阻塞UI渲染。以达到性能优化的目的。
normal
https://github.com/wuyunqiang/dynamic-tasks/assets/13480948/e3dd2bdc-ba3a-469d-b8eb-7fa2a68819d2
frame
https://github.com/wuyunqiang/dynamic-tasks/assets/13480948/d2d89081-5d89-4df9-8c3a-c22b7ca36956
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);
});
功能:
yieldToMain
让出主线程 避免长任务阻塞UI 造成页面卡顿 此方法后面的任务将在下一帧继续执行。 优先使用MessageChannel宏任务&无延迟 使用setTimeOut兼容处理
import {yieldToMain} from "dynamic-tasks";
p1()
await yieldToMain(); // 中断当前帧 让出给main thread 下一帧继续执行
p2();
sleep
import { sleep } from "dynamic-tasks";
console.log('111111')
await sleep(2000) // 两秒后继续执行
console.log('222222')
nextFrameExecute
下一帧执行某个任务 实现优先级: 1:使用MessageChannel宏任务方式 2:使用requestAnimationFrame 3:使用setTimeout做兼容。
import { nextFrameExecute } from "dynamic-tasks";
p1()
nextFrameExecute(p2) // 将在下一帧执行这个任务
p3()
DynamicTasks
有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)
},
},
]);
pool 支持web worker线程池
无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",
}
}
});
clearPool
import { clearPool } from "dynamic-tasks"
clearPool()
idleCallback
浏览器空闲执行 不紧急的任务建议使用这个api 不建议UI操作: 原因1:此时已经渲染完成,UI变更会导致页面重绘应尽量避免 原因2:因为调用时机不确定 dom操作会导致页面视觉变动难以预测 参考react fiber思路通过raf+messagechannel 对不支持requestidlecallback的浏览器做了polyfill。
import { idleCallback } from "dynamic-tasks"
idleCallback((params)=>{
console.log('test idleCallback params', params)
}, { timeout: 100})
idle
浏览器空闲执行 不紧急的任务建议使用这个api 不建议UI操作: 原因1:此时已经渲染完成,UI变更会导致页面重绘应尽量避免 原因2:因为调用时机不确定 dom操作会导致页面视觉变动难以预测 内部使用idleCallback方法。
import { idle } from "dynamic-tasks"
idle([{key: 'p1',task: p1}],100).then(res => {
console.log('test idle:', res)
})
serialTask
顺序执行一系列任务 并返回结果
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 }
]
parallelMaxTask
并发执行一系列任务并返回结果
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 }
]
TaskCancelable
封装的一个可以取消的promise任务
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();
KillAwait
Promise的同步调用 消除异步的传染性
import { KillAwait } from "dynamic-tasks"
const test3 = () => {
const res = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 2000);
});
console.log("test 3 res", res);
return res;
};
const test2 = () => {
const res = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 2000);
});
console.log("test 2 res", res);
return test3();
};
const test1 = () => {
const res1 = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
console.log("test 1 res1", res1);
const res2 = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(1.2);
}, 2000);
});
console.log("test 1 res2", res2);
return test2();
};
const main2 = () => {
const res = test1();
console.log("test 最终结果 res", res);
};
KillAwait.execute(main2);
// res
test 1 res1 1
test 1 res1 1
test 1 res2 1.2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 3 res 3
test 最终结果 res 3
microTask & macroTask
import { microTask, macroTask } from "dynamic-tasks"
microTask(()=>{
console.log('microTask')
})
macroTask(()=>{
console.log('macroTask')
})