异步的几种方式

149 阅读2分钟

浏览器内核的多线程

js引擎
Gui线程
浏览器事件触发线程

事件循环机制

任务队列中分为 macro-task(宏任务)与micro-task(微任务)

  1. macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

    浏览器发起宏任务

  2. micro-task【先执行】大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

    JS引擎发起微任务

    setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。
    事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的macro-task,这样一直循环下去。

    所以微任务会先执行,宏任务后执行,先promise()再setTimeout()

    回调函数(setTimout使用的就是回调函数)

    它是属于任务队列中的宏任务
    缺点:

    • 回调地狱
    • 不能捕获异常

    Promise函数

    它是属于任务队列中的微任务
    返回一个承诺对象,它会在某个特定的时刻执行,两个参数resolve()和reject()代表成功和失败;我们在需要执行异步的地方直接使用then()即可;

    优点:

    • 它将执行代码和处理逻辑的代码清晰的分离了

    缺点:

    • 书写不太方便,还是存在一定的回调感觉(.then().then())

    async和awit函数

    它是属于任务队列中的微任务
    async 函数必定返回 Promise,我们把所有返回 Promise 的函数都可以认为是异步函数。 async 函数是一种特殊语法,特征是在 function 关键字之前加上 async 关键字,这样, 就定义了一个 async 函数,我们可以在其中使用 await 来等待一个 Promise。
    优点:

    • 书写规范,允许嵌套,最优的异步实现方案

    缺点:

    • 兼容问题

    Generator 函数

    状态机,内部封装了多个状态

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';}

// 它返回的是一个指向内部状态的对象,一个遍历器对象
var hw = helloWorldGenerator();
// 调用next()方法,使得指针移动向下一个,value为内部状态的值。done表示遍历是否结束
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }