使用 async await + setTimeout 分割 while 循環

由於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>.&#10;

全部代碼如下:

<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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章