异步,EventLoop

564 阅读4分钟

单线程

众所周知,JS是单线程的语言,之所以是单线程,用一句烂大街的话就是,如果两个线程同时操作一个DOM节点,那么该以哪个为准呢,虽然多线程也有办法解决,但是js毕竟是浏览器脚本语言,不需要那么复杂

但是单线程遇到多个任务,需要排队执行,如果遇到定时器任务或者ajax请求等等,那会严重影响用户体验,于是将异步任务暂时挂起,先运行后面的任务,等异步操作返回了结果,再来执行
所以把任务分为两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous),同步任务是指在主线程上排队的任务,只有前一个任务执行完毕,才会执行后面的任务,异步任务是指不进入主线程,而是进入任务队列(task queue),当任务队列通知主线程某一个异步任务可以执行的时候,才会进入主线程执行

同步 异步执行机制

参考阮一峰大佬的博客,侵删

1 所有的同步任务都在主线程上执行,形成一个执行栈(execution context stack)
2 主线程之外,还有一个任务队列(task queue)
3 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,里面的异步任务就会结束等待状态,进入执行栈,开始执行
4 主线程会不断的重复上面的三部内容

那么js是如何处理异步呢:通过渲染进程(浏览器内核)多线程实现异步

进程和线程

阮一峰大佬的博客地址,侵删

进程

程序运行的实例,同一个程序可以产生多个进程,一个进程可以包含多个线程 线程 操作系统能够进行运算调度的最小单位,一个只能执行一个任务,有自己的调用栈,寄存器环境,同一个进程的线程共享进程资源

浏览器进程:Browser进程,渲染进程(浏览器内核),GPU进程,网络进程,插件进程

渲染进程(浏览器内核)

GUI线程

负责渲染页面,解析html,css,构建DOM树和渲染树,当界面需要重绘(repaint)或者由于某种操作引发回流(reflow)时,该线程就会执行 JS引擎线程 解析JS,和GUI互斥,因为JS也可以操作DOM,如果两个线程同时操作DOM,可能会出现不可预期的结果,所以JS引擎运行期间,GUI处于挂起状态,GUI更新会保存在一个队列中,等JS引擎空闲时,立即执行 定时器线程 定时任务是通过定时器线程运行,他会在定时任务完成之后,通知事件触发线程,往任务队列里添加事件 异步HTTP请求线程 用来处理AJAX请求,当请求完成时,如果有回调函数,就会通知事件触发线程处理 事件触发线程 将满足触发条件的事件,添加到任务队列的末尾

EventLoop

主线程从任务队列中读取事件,这个过程是不断循环的,称为EventLoop(事件循环)
下图可以更好的帮助理解eventLoop,图中的宏任务和微任务下面会说明

微任务 宏任务

微任务(microtasks)

Promise Object.observe(监听对象变化) mutationObserve(一个类,监听DOM结构变化) postMessage(window对象之间用来通信) 宏任务(macrotasks) script setTimeout setInterval setImmediate I/O UI rendering

异步任务分为宏任务和微任务,首先会执行script宏任务,然后执行完所有微任务之后才会执行宏任务
代码示例
需要注意的是,new Promise()里面的代码是一个参数,是同步执行的,then后面的代码才是异步执行

        console.log("start");
        setTimeout(() => {
            console.log("setTimeout1");
        });
        new Promise((resolve) => {
            console.log("Promise1");
            resolve();
        }).then(() => {
            console.log("then1");
            new Promise((resolve) => {
                resolve();
            }).then(() => {
                console.log("then2");
            });
            setTimeout(() => {
                console.log("setTimeout2");
            });
        });
        //结果start Promise1 then1 then2 setTimeout1 setTimeout2
        async function async1() {
            console.log("asyncStart1");
            await async2();
            console.log("asyncEnd1");
        }
        async function async2() {
            return Promise.resolve().then((res) => {
                console.log("asyncPromise2");
            });
        }
        console.log("start");
        setTimeout(() => {
            console.log("setTimeout");
        }, 0);
        async1();
        new Promise((resolve) => {
            console.log("promise1");
            resolve();
        }).then(() => {
            console.log("promise2");
        });
        // 结果 start asyncStart1  promise1 asyncPromise2 promise2 asyncEnd1 setTimeout

好的,这篇笔记到此结束,如果有错误或者更好的方法,希望看到的大佬赐教