07-异步

nobility 发布于 2022-12-04 01-ES5 1196 次阅读


异步

Js 只能在一个线程上运行,同一时间只能执行一个任务,其他任务需要在后面排队等,但是不代表 js 引擎只有一个线程,实际上 js 引擎有多个线程,单个脚本只能在一个线程上运行,其他线程都是在后台配合

  • 同步任务:指在主线程上排队执行的任务;

  • 异步任务:指暂时被 js 引擎挂起放入任务队列中,当 js 引擎认为任务队列中的某个任务可以执行了,该任务才会进入主线程执行

    • js 引擎执行完所有的同步任务后,先查看微队列中是否有微任务,有就全部执行,微队列中的所有微任务都执行完毕后,查看宏队列中是否有宏任务,有就执行一个宏任务,执行完之后,回过头查看微队列中是否有微任务,有就全部执行;换句话说就是,每次执行宏任务之前都会将所有可执行的微任务都执行完

    • 宏任务:存放在宏队列中,包含定时器回调函数、AJAX 请求回调函数、DOM 事件回调函数

    • 微任务:存放在微队列中包含 promise 回调函数

  • 事件轮询机制:只要主线程的同步任务执行完毕,就会一遍一遍的检测哪些挂起的在异步队列中的任务是否可以进入主线程执行了

定时器

方法名 描述
setTimeout (fun[, delay,... Args]) 一次性定时器,返回定时器 id;省略delay默认是 0,args是该回调函数的参数列表
clearTimeout (id) 通过定时器 id 清除一次性定时器
setInterval (fun[, delay,... Args]) 循环定时器,返回定时器 id;省略delay默认是 0,args是该回调函数的参数列表
clearInterval (id) 通过定时器 id 清除循环定时器
requestAnimationFrame (fun) 请求动画帧,返回定时器 id;仅执行一次,想执行动画需要在回调中递归的调用;由系统决定回调函数的执行时机,一般是 60 Hz 的刷新频率,解决了定时器的丢帧,卡顿现象
cancelAnimationFrame (id) 通过定时器 id 清除请求动画帧

串行执行多个异步任务

为了保证多个异步任务一个接一个执行,并且上一个异步任务处理的结果再交由下一次异步任务处理,对于这种情况就可能出现多个异步回调函数嵌套,称之为回调地狱(callback hell),难以维护

SetTimeout (function () {
    Console.Log (1);
    SetTimeout (function () {
        Console.Log (2);
        SetTimeout (function () {
            Console.Log (3);
        }, 1000)
    }, 1000)
}, 1000)
//1
//2
//3
//一秒输出一个数字,该程序执行 3 秒结束

为了多个异步任务串行执行,其实就是在一个异步任务中调用另一个异步任务,编写一个异步任务控制流程函数来避免回调地狱的出现

/*
Value: 第一次函数的 input 值
Async:函数队列,要求形式是 async (input, output)
    Input 上一次函数的输出这一次的输入
    Output 是回调函数,用于向下一个函数传递当前函数处理的结果,也就是说将处理结果通过 output 输出到下一个函数中
功能:依次执行函数队列中的函数,并且上一次函数的输出是这一次函数的输入,多个函数串行执行,包括异步函数
*/
Function autoRun (value, ... Async) {	//es 5 不支持剩余参数可以使用 arguments 对象
    /*
    Input: 上一次函数的输出,这次函数的输入
    功能:执行一个函数队列中的一个函数
    */
    Function next (input) {
        If (typeof async[0] === "function") {    //若函数队列的第一个元素是函数才能执行
            Async[0](input, function (handle) { //该回调用于向下一个函数输入处理后的结果和调用队列中的下一个函数
                Async.Shift ();  //函数队列弹出一个执行完毕的函数
                Next (handle); //调用函数队列中的下一个函数,并将处理后的数据传入到下一个函数的输入参数
            })
        }
    }
    Next (value);    //启动执行函数队列
}

//使用方式
AutoRun (1,
    Function (input, output) {
        SetTimeout (() => {
            Console.Log (input);
            Output (++input);
        }, 1000);
    },
    Function (input, output) {
        SetTimeout (() => {
            Console.Log (input);
            Output (++input);
        }, 1000);
    },
    Function (input, output) {
        SetTimeout (() => {
            Console.Log (input);
        }, 1000);
    });
//会依次打印 1 2 3
加油啊!即便没有转生到异世界,也要拿出真本事!!!\(`Δ’)/
最后更新于 2022-12-04