[ES6] 异步处理

JavaScript025

[ES6] 异步处理,第1张

JS运行的环境称之为宿主环境

执行栈: call stack ,一个数据结构,用于存放各种函数的执行环境,每一个函数执行之前,它的相关信息会加入到执行栈。函数调用之前,创建执行环境,然后加入到执行栈;函数调用之后,销毁执行环境。

JS引擎永远执行的是执行栈的最顶部。

异步函数:某些函数不会立即执行,需要等到某个时机到达后才会执行,这样的函数称之为异步函数。比如事件处理函数。异步函数的执行时机,会被宿主环境控制。

浏览器宿主环境中包含5个线程:

当上面的线程发生了某些事情,如果该线程发现,这件事情也有处理程序,它会将该处理程序加入一个叫做事件队列的内存。当JS引擎发现,执行栈中已经没有了任何内容后,会将事件队列中的第一个函数加入到执行栈中执行。

JS引擎对事件队列的取出执行方式,以及与宿主环境的配合,称之为事件循环。

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。在浏览器中,事件队列分为两种:

MutationObserver用于监听某个DOM对象的变化

当执行栈清空时,JS引擎首先会将微任务中的所有任务依次执行结束,如果没有微任务,则执行宏任务。

我们习惯于使用传统的回调或事件处理来解决异步问题

事件:某个对象的属性是一个函数,当发生某一件事时,运行该函数

回调:运行某个函数以实现某个功能的时候,传入一个函数作为参数,当发生某件事的时候,会运行该函数

本质上,事件和回调并没有本质的区别,只是把函数放置的位置不同而已。

一直以来,该模式都运作良好。

目前,该模式主要面临以下两个问题:

ES官方参考了大量的异步场景,总结出了一套异步的通用模型,该模型可以覆盖几乎所有的异步场景,甚至是同步场景

值得注意的是,为了兼容旧系统,ES6并不打算抛弃掉过去的做法,只是基于该模型推出一个全新的API,使用该API,会让异步处理更加的简洁优雅。

理解该API,最重要的是理解它的异步模型

事情总是从 未决阶段 逐步发展到 已决阶段 的。并且,未决阶段拥有控制何时通向已决阶段的能力。

既然未决阶段有权利决定事情走向,因此,未决阶段可以决定事情最终的状态

我们将把事情变为 resolved 状态的过程叫做: resolve ,推向该状态时,可能会传递一些数据

我们将把事情变为 rejected 状态的过程叫做: reject ,推向该状态时,同样可能会传递一些数据,通常为错误信息

后续处理可能有多个,因此会形成作业队列,这些后续处理会按照顺序,当状态到达后依次执行

细节

当后续的 Promise 需要用到之前的Promise的处理结果时,需要 Promise 串联

Promise 对象中,无论时 then 方法还是 catch 方法,它们都具有返回值,返回的是一个全新的 Promise 对象,它的状态满足下面的规则:

后续的 Promise 一定会等到前面的 Promise 有了后续处理结果后,才会变成已决状态

async 和 await 是ES2016新增的两个关键字,它们借鉴了ES2015 中生成器的实际开发中的应用,目的是简化Promise API 的使用,并非是替代了 Promise

async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置, 被修饰的函数返回结果一定是 Promise 对象。

await关键字必须出现在async函数中

await用在某个表达式之前,如果表达式是一个 Promise ,则得到的是 thenable 中的状态数据

JS引擎的主线程负责执行代码,由于只有这一个线程,执行当然是同步的,即按照顺序来。另外,还有一个叫做任务队列的东西,所有的异步代码都是从队列当中来。

所以实际上我们会发现,JS根本不可能同时执行两个任务,本质上还是单线程。

在JS中,所谓的异步任务,有三种:

第一, 鼠标键盘事件触发,例如onclick、onkeydown等等

第二, 网络事件触发,例如onload、onerror等等

第三, 定时器,例如setTimeout、setInterval