Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

magix5中的task与lowTask #107

Open
xinglie opened this issue Apr 28, 2023 · 0 comments
Open

magix5中的task与lowTask #107

xinglie opened this issue Apr 28, 2023 · 0 comments

Comments

@xinglie
Copy link
Owner

xinglie commented Apr 28, 2023

任何代码的执行都是需要时间的

在浏览器环境下,DOM的操作尤其费时间,所以才会有各种各样的库和框架以求最小化的变更来操作DOM提升效率。

但是在条件一定的情况下,就是要操作许多费时的DOM我们该如何避免浏览器卡死或卡顿呢?

假如页面上有10000div,我们需要遍历它们并旋转,再把外接矩形的信息放在div内部,我们可能这样写

let divs = document.querySelectAll('div');
let deg = 0;
for(let div of divs){
    div.style.transform = `rotate(${deg++%360}deg)`;
    let bound = div.getBoundingClientRect();
    div.innerHTML = `x:${bound.x},y:${bound.y},width:${bound.width},height:${bound.height}`;
}

我们进行性能放大来看:假如每个div旋转并获取外接矩形所需要的时间是1ms,那么完成整个操作就需要10000ms,在这个时间内,浏览器是卡住的,这段时间内用户无法操作浏览器。

我们该如何即快速完成DOM操作又不卡浏览器呢?

其实在单线程的DOM操作上,我们必须去做转换才能解决问题,否则是无解的。

我们需要做的第一件事就是把操作进行方法封装,比如这样

let process = (div, deg) => {
    div.style.transform = `rotate(${deg%360}deg)`;
    let bound = div.getBoundingClientRect();
    div.innerHTML = `x:${bound.x},y:${bound.y},width:${bound.width},height:${bound.height}`;
};

let divs = document.querySelectAll('div');
let deg = 0;
for(let div of divs){
    process(div, deg++);
}

这样转换之后依然会有10000ms的卡住时间。那么接下来就需要使用magix5提供的task方法了,如下

import Magix from 'magix5';
let {task} = Magix;
let process = (div, deg) => {
    div.style.transform = `rotate(${deg%360}deg)`;
    let bound = div.getBoundingClientRect();
    div.innerHTML = `x:${bound.x},y:${bound.y},width:${bound.width},height:${bound.height}`;
};

let divs = document.querySelectAll('div');
let deg = 0;
for(let div of divs){
    task(process, div, deg++);
}

我们只需要把原来的process(div, deg++);换成task(process, div, deg++);magix5内部会自动根据浏览器性能,最大化的去异步执行process方法,不卡浏览器。

magix5中提供2种优先级的任务,正常优先级的task及低优先级的lowTask

import Magix from 'magix5';
let {task,lowTask} = Magix;
task(()=>{
   console.log('task 1 complete');
});
lowTask(()=>{
   console.log('low task complete');
});
task(()=>{
    console.log('task 2 complete');
});

以上代码会输出

// task 1 complete
// task 2 complete
// low task complete

当同时存在tasklowTask任务时,lowTask始终在最后被执行,您可以把一些低优先级且耗时的任务放在lowTask里。

那么如何知道tasklowTask执行完了呢?

我们可以直接使用taskFinalelowTaskFinale这2个方法,如下

import Magix from 'magix5';
let {task,taskFinale} = Magix;
let process = (div, deg) => {
    div.style.transform = `rotate(${deg%360}deg)`;
    let bound = div.getBoundingClientRect();
    div.innerHTML = `x:${bound.x},y:${bound.y},width:${bound.width},height:${bound.height}`;
};

let divs = document.querySelectAll('div');
let deg = 0;
for(let div of divs){
    task(process, div, deg++);
}
console.log('all div push to task');//这里仅仅是把任务安排上,尚未执行完所有的task
await taskFinale();
console.log('all task finished');//所有task执行完成

lowTaskFinaletaskFinale相同,只不过它是确保所有低优先级的任务完成。

如果要确保所有任务完成,可使用lowTaskFinale,如

import Magix from 'magix5';
let {task,lowTask,lowTaskFinale} = Magix;
task(()=>{
   console.log('task 1 complete');
});
lowTask(()=>{
   console.log('low task complete');
});
task(()=>{
    console.log('task 2 complete');
});
await lowTaskFinale();
console.log('all task finished');

扩展

除了上述的task, lowTask, taskFinale, lowTaskFinale4个核心的方法外,还有一个扩展方法taskIdle,在任务队列空闲至少多少毫秒后执行,如

import Magix from 'magix5';
let {task,lowTask,taskIdle} = Magix;
task(()=>{
   console.log('task 1 complete');
});
lowTask(()=>{
   console.log('low task complete');
});
task(()=>{
    console.log('task 2 complete');
});
await taskIdle(500);
console.log('browser idle');

在给定时间内,没有要执行的task才会执行后面的代码,否则只要有任务进来,taskIdle就会继续挂起。

需要注意的是这些方法是全局共用的,即一个view中调用task,也会影响其它viewtask调用的执行和回调时间,不过这些只是先后顺序而已,不会影响最终结果。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant