异步方案研究小结

同步任务与异步任务

同步任务:在主线程上排队执行的任务,按序执行调用,比如渲染页面的元素
异步任务:不仅如此主线程,而是进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行,比如某一张图片的加载,某一段音乐的加载

异步的执行方式

JS在处理任务的时候,可以分为同步任务和异步任务。只有当同步任务都执行完毕之后,才会到任务队列里面执行异步任务。
执行流程

异步任务执行的顺序是按事件循环的结果,执行的方式是回调函数

回调函数

一个异步任务被添加到任务队列中,等到异步操作完成后,就会在任务队列中添加一个事件,表示异步任务完成准备,可以进入执行栈,等主线程有空时,就会读取任务队列,执行里面的任务,如果该任务指定了回调函数,那么主线程在处理该事件时,就会执行回调函数中的代码,即执行异步任务。

事件循环

在任务队列中,也分为宏任务和微任务分别在宏任务队列和微任务队列,只有微任务队列中的任务全部执行完毕后,才会执行宏任务队列里面的任务。

宏任务:当前调用栈中执行的代码成为宏任务。script setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。process.nextTick, Promises, Object.observe, MutationObserver

每一次事件循环触发时:
先执行同步代码,
遇到异步宏任务则将异步宏任务放入宏任务队列中,
遇到异步微任务则将异步微任务放入微任务队列中,
当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,
微任务执行完毕后再将异步宏任务从队列中调入主线程执行,
一直循环直至所有任务执行完毕。
注意:当宏任务和微任务都处于 任务队列(Task Queue) 中时,微任务的优先级大于宏任务,即先将微任务执行完,再执行宏任务;

异步的其他解决方案

回调函数

在上文已经提到过,不多赘述。他的优点是简单、易读、易实现,缺点是增加了维护的成本,提高了各部分的耦合度,让结构变得混乱,流程难以追踪(特别是出现回调嵌套的情况)。并且每个任务只能指定一个回调函数,不能用trycatch捕获异常。

事件监听

异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
通用性的事件监听方法:

  • 绑定HTML元素属性
  • 绑定DOM对象属性:document.getElementById(“xxx”).οnclick=test;
    在DOM2级之后,事件监听函数为addEventListener,有三个参数:
    第一个参数:事件的类型(DOM0绑定事件是直接写onclick,DOM2绑定事件的时候,要省略on)
    第二个参数:绑定的监听者(个人理解就是回调函数=>也就是点击时要执行的函数)
    第三个参数:是一个boolean类型的值(true:表示方法在捕获阶段时执行;false:表示在冒泡传播阶段执行)

发布订阅

存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。

Generators/yield

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同,最大的特点就是可以控制函数的执行。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态,还是一个遍历器对象生成函数。
可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

Promise

Promise是承诺的意思,承诺它过一段时间会给你一个结果。
它是一种解决异步编程的方案。
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
promise有三种状态:
pending 初始状态也叫等待状态
fulfiled成功状态
rejected失败状态
状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

主要特点

  1. Promise对象的状态不受外界影响
  2. Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆。

缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反映到外部
  3. 当处于pending(等待)状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

应用场景

  1. 回调嵌套
  2. 多并发请求,获取并发请求的数据

常用API

  • Promise.resolve() 默认产生一个成功的 promise。

  • Promise.reject() 默认产生一个失败的 promise,Promise.reject 是直接将值变成错误结果。

  • Promise.prototype.catch() 用来捕获 promise 的异常,就相当于一个没有成功的 then

  • Promise.prototype.finally() 如果返回一个 promise ,会等待这个 promise 也执行完毕。如果返回的是成功的 promise,会采用上一次的结果;如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。

  • Promise.all() 用来解决并发问题,多个异步并发获取最终的结果(如果有一个失败则失败)。

  • Promise.race() 用来处理多个请求,采用最快的(谁先完成用谁的)。

async/await

用同步的方式,编写异步。
async/await是基于Promise和Generators实现的,它不能用于普通的回调函数。
async和await 就是 Generators和 Promise的组合语法糖,将 generator 的 * 换成async,将 yield 换成await。

用法细节

  1. 函数前必须加一个 async,异步操作方法前加一个 await 关键字
  2. await 只能在 async 函数中运行,否则会报错
  3. Promise 如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以用 try..catch 捕获一下异常就可以了。
  4. await后面是一个pormise或者async函数会造成异步函数停止执行并且等待 promise 的解决,
  5. await后面是 正常的表达式则立即执行。

应用场景

  1. 连续相互依赖的请求
  2. 并发执行请求
  3. 错误处理
  4. 超时处理
  5. 并发限制

你可能感兴趣的:(异步方案研究小结)