JS 异步同步 阻塞非阻塞 事件循环 微任务 宏任务

线程

javascript是一门 单线程、异步、非阻塞、弱类型脚本语言。

JS的单线程是指一个浏览器进程中只有一个JS的执行线程,同一时刻内只会有一段代码在执行,但是浏览器的渲染进程是多线程的,而异步机制是浏览器的两个或以上常驻线程共同完成的。

同步异步 阻塞非阻塞

老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
——来源网络,作者不明。

事件循环

首先简单了解JS执行顺序
step 1. 读入第一个代码块。 
step 2. 语法分析,有错则报语法错误,并跳转到step5。  
step 3. 对var变量和function定义做“预编译处理”(预解析)。  
step 4. 执行代码段,有错则报错(比如变量未定义)。  
step 5. 如果还有下一个代码段,则读入下一个代码段,重复step2。  
step 6. 结束。
事件循环
image

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  3. 一但"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。

5.只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复,这种机制就被称为事件循环(event loop)机制

宏任务 微任务

image

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

宏任务 macro-task(Task)

一个event loop有一个或者多个task队列。task任务源非常宽泛,比如ajax的onload,click事件,基本上我们经常绑定的各种事件都是task任务源,还有数据库操作(IndexedDB ),需要注意的是setTimeout、setInterval、setImmediate也是task任务源。总结来说task任务源:

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • requestAnimationFrame
  • UI rendering

微任务 micro-task(Job)

microtask 队列和task 队列有些相似,都是先进先出的队列,由指定的任务源去提供任务,不同的是一个 event loop里只有一个microtask 队列。另外microtask执行时机和Macrotasks也有所差异

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

宏任务和微任务的区别

  • 宏队列可以有多个,微任务队列只有一个,所以每创建一个新的settimeout都是一个新的宏任务队列,执行完一个宏任务队列后,都会去checkpoint 微任务。
  • 一个事件循环后,微任务队列执行完了,再执行宏任务队列
  • 一个事件循环中,在执行完一个宏队列之后,就会去check 微任务队列

自我提示

console.log("start")

  setTimeout(()=>{
        console.log(5)
    },0)
    
    Promise.resolve().then(()=>{
        setTimeout(()=>{
            console.log(1)
        },0)

        console.log(2)
    }).then(()=>{
        console.log(3)
    })
    console.log(4)

    // process.nextTick(()=>{
    //     console.log(6)
    // })
    console.log("end")

你可能感兴趣的