V8-学习4-消息循环-消息队列/promise/微任务/协程/async

119 阅读4分钟

1.消息队列-宏任务

回调函数类型

同步回调

函数内部执行

function a(){console.log("11")}
function b(fun) { //这里b执行的a方法是同步回调方法
     fun()
}
a()

异步回调

函数外执行,基于消息队列触发执行

function a(){console.log("11")}
settimeout( a,1000)//这里settimeout 执行的a方法是异步回调方法

消息队列

可以理解为一个死循环,不断读取任务并执行

异步回调流程

  • XMLHttpRequest
    1. 主线程在js代码发起XMLHttpRequest 请求,通过IPC通知网络进程下载
    2. 网络进程不断读取服务端返回的数据,每次接收信息都会把回调函数加入 消息队列。(包含字节大小等)
    3. 事件循环依次读取宏任务,开发可以根据是下载的回调,做响应的处理
    4. 当下载结束也会写入 下载完成的任务到消息队列,开发可以在回调中处理完成逻辑
  • setTimeout
    1. 生成回调任务添加到消息队列
    2. 消息队列依次读取宏任务执行

2.微任务

优势

解决宏任务颗粒度不够细问题,可以更加精准控制任务执行的顺序

js主线程执行过程

  • 主要组成
    • 调用栈
    • 主线程
    • 消息队列
  • 流程
    • 主线程一直循环 读取 消息队列宏任务,每执行完一个宏任务,都把当前生成的微任务清空掉,挨个执行。

微任务执行时机

在当前主函数 执行析构函数时

嵌套调用问题

  • 同步回调
    • 会导致栈溢出,因为都在同一个宏任务中执行函数,栈会不断嵌套累加
fun(){
  console.log("111")
   fun()
}
fun()
  • settimout
    • 回调函数是基于消息队列,下一个宏任务调用时,上一个宏任务已经完全执行完,栈已被清空
fun(){
  console.log("111")
}
settimeout(fun)
  • Promise.then()
    • 虽然不会提示栈溢出,但是会卡死,因为执行完宏任务,都会清空执行所有微任务,清空时微任务已经清空所有调用栈
    • 但是嵌套再调用微任务又会继续生成微任务队列,并执行。导致下一个宏任务一直无法开始
function fun() {
    console.log("111")
    Promise.resole().then(fun)
}
fun()

MutationObserver的工作原理

  1. 生成微任务,不会阻塞主程序执行
  2. 多个事件监听只触发一次,并且返回数组来描述不同的变化事件
  3. 通过options参数传入指定的监听类型

3.协程-async-await

地狱回调

  • XMLHttpRequest有很多注册的回调方法,代码阅读时顺序是错乱的
  • 编码线性问题
    1. promise方案
      • 大量的promise.then(),then()依然导致阅读困难,语义化不明显
    2. 协程+ Generator + co执行器 + promise方案
      • 实现的函数的暂停和恢复执行,但是引入太多臃俞的代码
    3. async await
      • 代码最直观线性

协程

  • 协程是运算在线程上的多个任务
  • 1个线程包含多个协程
  • 一个线程只能同时运行一个协程
  • A启动B,A就是B的父协程

例子

function* getResult() {
    console.log(1111)
    yield '11'
    console.log(2222)
    yield '22'
    console.log(3333)
    return '33'
}
let result = getResult()
console.log(result.next().value)
console.log(result.next().value)
console.log(result.next().value)

async await

例子

async function main(){
let a = await Promise.resolve("111")
console.log(a)
console.log("222")
}
main()
console.log("333")

// 输出
// 333 //因为最外面的代码没有被包裹 async,依然不会被等待
// 111
// 222

原理

  1. 被async包裹的函数会被异步执行,可以理解方法已经变成 Generator函数 带*
  2. async函数里使用 await,等价于调用了yield '11',把当前协程挂起,并执行await后面的异步代码
  3. 等待异步代码返回resolve成功后,会自动执行next() 交回给当前协程继续执行代码

async

异步执行并隐式返回 Promise

await

  1. 后面支持一个返回promise对象
  2. 可以是非promise对象或为空,系统会自动包裹一个Promise.resolve()

扩展co的源码实现