EventLoop 的理解

栏目: 后端 · 发布时间: 5年前

内容简介:一般前端了解eventloop的时候,##一、总结ps:对纯前端而言,掌握Promise.prototype.then、async await 和 setTimeout 的区别就可以了。另外,Promise、async await是可以转换的,但是浏览器版本问题,async await 的优先级可能高于 Promise(

一般前端了解eventloop的时候, 最想知道代码执行的先后顺序 ,并非分析EventLoop。 所以这里先说总结 。 为什么?因为面试常考这个:joy:,因为分析和听懂分析都费劲。

##一、总结

  1. js 单线程缺点,容易出现"假死"(如alert()之后,dom不渲染)。优点:保证 dom 的渲染不易出错。
  2. 解决"假死"的问题?其他语言,如 java 采用多线程去解决,避免占用计算机空间太大,dom 渲染问题易出错问题存在。所以js 采用单线程+异步解决方案。
  3. 单线程 + 异步的实现方式叫做——EventLoop
  4. 加入 异步队列 方式有两种,第一种是宏任务;第二种是微任务。 微任务加入异步队列的优先级宏任务,且是先进先出原则 ,说得比较绕,简而言之就是, 微任务执行完,才会执行宏任务
  5. 微任务:Promise.prototype.then、async await、Process.nextTick(Node独有)、Object.observe(废弃)、MutationObserver
  6. 宏任务:script全部代码、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/O、UI Rendering。
  7. 执行的优先级即, 同步函数(主线程上) -> 异步队列·微任务 -> 异步队列·宏任务

ps:对纯前端而言,掌握Promise.prototype.then、async await 和 setTimeout 的区别就可以了。另外,Promise、async await是可以转换的,但是浏览器版本问题,async await 的优先级可能高于 Promise( 可以忽略这种情况 )。

测试题目:

const fn1 = await function () {
	await fn2()
	console.log(1)
}

async function fn2 () {
	await console.log(2)
}

setTimeout(function () {
	console.log(3)
})

new Promise(function (resolve, reject) {
	console.log(4)
	resovle()
}).then(function () {
	console.log(5)
}).then(function () {
	console.log(6)
})
// 答案
// 2
// 4
// 1
// 5
// 6
// 3
复制代码

分析:

  1. 2 和 4 是同步函数
  2. 1、5 和 6 是微任务,异步队列的顺序是1、5、6
  3. 3 是宏任务

代码转换分析

function fn1 () {
	new Promise(function(resolve, reject) {
		console.log(2)
		resolve()
	}).then(function () {
		console.log(1)
	})
}

fn1()

setTimeout(function () {
	console.log(3)
})

new Promise(function (resolve) {
	console.log(4)
	resolve()
}).then(function () {
	console.log(5)
}).then(function () {
	console.log(6)
})
复制代码

二、内存分析和缘由

阮一峰eventloop www.ruanyifeng.com/blog/2013/1… 看内存分析的这篇 github.com/baiyuze/not…

了解过,可以跳过

三、封装Promise源码分析

看这篇: juejin.im/entry/59996… 觉得分析有点啰嗦,没有提取关键信息

了解过,可以跳过

四、uml 类图分析Promise

MDN文档 的 Promise 执行流程图:

EventLoop 的理解

第 1 步 用到的 设计模式 和 理解 promise 的简易结构

了解代码第一步,一定要知道他的设计模式,能够节约相当看代码时间。Promise 代码 最主要的模式, 观察者模式

EventLoop 的理解

封装一个简易的Promise的 resolvethen ,便于理解 Pomise 的封装解构

  • then 从语法结构上讲是然后的意思,但在封装上,是将函数加入异步队列,并返回一个 Promise 的一个类似对象
  • resolve 执行异步队列
class Que {
  _queueLit = []
  constructor (handler) {
    handler.call(this, this._resolve)
  }
  _queue (cb) {
    setTimeout(cb)
  }
  _resolve = () => {
    const { _queueLit, _queue } = this
    const resolve = function () {
      let cb
      while (cb = _queueLit.shift()) {
        cb()
      }
    }
    _queue(resolve)
  }
  then (cb) {
    this._queueLit.push(cb)
    return this
  }
}

// test
setTimeout(() => {
  console.log(4) 
});
new Que(function (resolve) {
  console.log(1)
  resolve()
}).then(function () {
  console.log(2)
}).then(function () {
  console.log(3)
})

// 结果
// 1
// 4
// 2
// 3
复制代码

后记:不然发现 setTimeout 执行的 callback 为什么是全局的,以及 await 为什么不能在全局环境下,只能在函数内?原因是为了加入异步队列。

第 2 步 promise 处理8关键点(第 3 点 最为重要)

上面的封装的代码,简单理解 Promise 封装的解构,现在梳理 Promise的解构,可知道 promise 的根本的两个方法: then 和 _resolve , _queues、_status。reject 、catch等都是在此基础上上进行二次封装。下列数列梳理几个点:

  1. then 接受的函数,是 Promise 时的处理方式
  2. then 接受的函数,是同步函数时的处理方式
  3. then 返回一个 Promise 实例,将该实例要执行的 _resolve 放到上一个 Promise 实例的异步队列中即将执行的异步函数中
  4. _resolve 传递参数的value 是一个Promise 时的处理方式
  5. _resolve 放到 eventloop 中,即 setTimeout(run) 中 ,先进先出执行异步队列
  6. _status 状态改变只在 _resolve 中改变
  7. _status 状态判断只在then中执行
  8. 利用自责链之后,每个Promise 的 _queues 只有一个元素, _queues 是里面是多个观察者,这里根据其他人说的,要实现一个 1 对 1 的观察者模式。

第 3 步 从完整版的Promise提取关键代码

源码地址--> coderlt.coding.me/2016/12/04/…

uml 类图

EventLoop 的理解

拿到源码,将源码错误检测、不需要分析的方法通通干掉,避免混淆视听,得到如下代码:

// 判断变量否为function
const isFunction = variable => typeof variable === 'function'

const StatusType = {
  PENDING: 'PENDING',
  FULFILLED: 'FULFILLED',
}

class MyPromise {
  _status = StatusType.PENDING // 添加状态
  _value = undefined // 添加状态
  _fulfilledQueues = [] // 添加成功回调函数队列
  constructor(handle) {
    handle(this._resolve.bind(this))
  }
  _resolve(val) {
    const run = () => {
      if (this._status !== StatusType.PENDING) return
      const runFulfilled = (value) => {
        let cb
        while (cb = this._fulfilledQueues.shift()) {
          cb(value)
        }
      }
      if (val instanceof MyPromise) {
        const resolvePromise = val
        resolvePromise.then(value => {
          this._value = value
          this._status = StatusType.FULFILLED
          runFulfilled(value)
        })
      } else {
        this._value = val
        this._status = StatusType.FULFILLED
        runFulfilled(val)
      }
    }
    setTimeout(run)
  }

  then(onFulfilled) {
    const {
      _value,
      _status
    } = this
    // 返回一个新的Promise对象
    return new MyPromise((onFulfilledNext) => {
      // 封装一个成功时执行的函数
      let fulfilled = value => {
        let res = onFulfilled(value)
        if (res instanceof MyPromise) {
          res.then(onFulfilledNext)
        } else {
          // 下一个 promise 的 resolve 方法的执行
          onFulfilledNext(res)
        }
      }
      switch (_status) {
        case StatusType.PENDING:
          /* 至关重要的代码 */
          this._fulfilledQueues.push(fulfilled)
           /* 至关重要的代码 end */
          break
        case StatusType.FULFILLED:
          fulfilled(_value)
          break
      }
    })
  }
}
复制代码

以上所述就是小编给大家介绍的《EventLoop 的理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码的未来

代码的未来

[日] 松本行弘 / 周自恒 / 人民邮电出版社 / 2013-6 / 79.00元

《代码的未来》是Ruby之父松本行弘的又一力作。作者对云计算、大数据时代下的各种编程语言以及相关技术进行了剖析,并对编程语言的未来发展趋势做出预测,内容涉及Go、VoltDB、node.js、CoffeeScript、Dart、MongoDB、摩尔定律、编程语言、多核、NoSQL等当今备受关注的话题。   《代码的未来》面向各层次程序设计人员和编程爱好者,也可供相关技术人员参考。一起来看看 《代码的未来》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具