由于JavaScript单线程的特性,在执行CPU密集型任务时(如大数组排序)会阻塞网页的渲染,解决办法一般是使用setTimeout函数对任务进行分割,或者把这类任务交由 web worker 处理。今天遇到一个对大数组排序的问题,需要使用任务分割来避免阻塞,在这里记录下我的实现方法。在实现时我将 while 语句进行了分割,避免出现大循环体语句一直进行计算,主要代码如下:
// 将 while 控制语句进行分割
// expression 相当于 while 的条件判断
// statement 相当于 while 循环体语句
async function while_async(expression, statement){
while(expression()){
await while_seg(expression, statement);
}
}
let while_seg = (expression, statement) => new Promise((resolve, reject) => {
setTimeout(()=>{
let i = 0;
let while_size = 50000;
while(expression()){
i++;
statement();
// 如果 while 内循环次数大于 while_size ,则进入事件循环
if(i>while_size)break;
}
resolve();
}, 0);
});
应用在大数组排序上,效果对比可以在这里查看:https://codepen.io/liuyaqi/pen/dmZdPe
height="351" width="650" scrolling="no" title="while_seg" src="//codepen.io/liuyaqi/embed/dmZdPe/?height=351&theme-id=0&default-tab=result&embed-version=2" allowfullscreen="true">See the Pen <a href="https://codepen.io/liuyaqi/pen/dmZdPe/">while_seg</a> by liuyaqi (<a href="https://codepen.io/liuyaqi">@liuyaqi</a>) on <a href="https://codepen.io">CodePen</a>. 全部代码如下:
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div{
width: 100px;
height: 100px;
margin-left: 0;
background-color: aqua;
animation: animation 3s infinite ;
}
div:hover{
background-color: white;
}
@keyframes animation {
0% {
margin-left: 0;
}
50% {
margin-left: 500px;
}
100% {
margin-left: 0;
}
}
</style>
</head>
<body>
<div></div>
<button id="sort">sort</button>
<button id="s_sort">setTimeout+sort</button>
</body>
<script>
let data = [];
let size = 2000000;
for(let i=0; i<size; i++){
data.push(Math.floor(Math.random()*size));
}
console.log(data);
document.querySelector("#sort").onclick = ()=>{
data.sort((a, b) => a-b);
console.log("排序结果", data);
alert("done");
}
document.querySelector("#s_sort").onclick = async ()=>{
let res = await mergeSort_async(data);
console.log("排序结果", res);
alert("done");
}
// 归并排序算法
async function mergeSort_async(array){
var length = array.length;
// 如果数组长度小于10000,直接排序
if(length<10000){
return array.sort((a, b) => a-b);
}
var mid = Math.floor(length / 2),
left = array.slice(0, mid),
right = array.slice(mid, length);
return await merge_async(await mergeSort_async(left), await mergeSort_async(right));
}
async function merge_async(left, right){
var result = [],
il = 0,
ir = 0;
var args = {il, ir, result, left, right};
await while_async(()=>(il < left.length && ir < right.length), ()=>{
if(left[il] < right[ir]){
result.push(left[il++]);
}else{
result.push(right[ir++]);
}
});
await while_async(()=>(il < left.length), ()=>{
result.push(left[il++]);
});
await while_async(()=>(ir < right.length), ()=>{
result.push(right[ir++]);
});
return result;
}
// 将 while 控制语句进行分割
// expression 相当于 while 的条件判断
// statement 相当于 while 循环体语句
async function while_async(expression, statement){
while(expression()){
await while_seg(expression, statement);
}
}
let while_seg = (expression, statement) => new Promise((resolve, reject) => {
setTimeout(()=>{
let i = 0;
let while_size = 50000;
while(expression()){
i++;
statement();
// 如果 while 内循环次数大于 while_size ,则进入事件循环
if(i>while_size)break;
}
resolve();
}, 0);
});
</script>
</html>