你不知道的JS系列-5

概述

本篇开始,记录在阅读《你不知道的JavaScript-中卷》中遇到的自己遗漏的知识点,阅读章节为第二部分:异步与性能

异步发展史

ES6 之前,JavaScript 中发异步编程主要就是靠回调函数来完成的。ES6 出现后,JavaScript 才算真正内建有直接的异步概念。

线程与进程

以Chrome为例,启动后,只要访问任意网站,至少要启动:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。我们也经常听到进程与线程的概念,此处,做下简单说明:

  • 进程:一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。
  • 线程:线程是不能单独存在的,它是由进程来启动和管理的。线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。

Event Loop

对于Chrome浏览器来说,每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是事件循环系统(Event Loop)。Event Loop中的任务在每个tick中是顺序执行的(一次只能从队列中处理一个事件),不会存在并行的可能。

关于Event Loop 可参考之前的文章:Nodejs系列-2-EventLoop

回调函数

大脑对于事情的计划方式是线性的、阻塞的、单线程的语义,但是回调表达异步流程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大

回调函数这种异步模式最大的问题在于:控制反转,它会导致信任链的完全断裂。也就是将程序一部分的执行控制交给了某个异步程序去执行,执行过程可能会发生下面各种异常状况(包括但不限于):

  • 调用回调过早(在追踪之前);
  • 调用回调过晚(或没有调用);
  • 调用回调的次数太少或太多(就像你遇到过的问题!);
  • 没有把所需的环境 / 参数成功传给你的回调函数;
  • 吞掉可能出现的错误或异常;

而嵌套的回调方式被称之为:回调地狱(callback hell),

Promise

由于回调函数本身存在的问题,ES6标准新增了另外一种异步解决方案:Promise。关于Promise的介绍可参考ES6系列-6-异步

这里我们只记录几点自己遗漏的知识点:

  • 在回调中,一些模式化的错误处理方式已经出现,最值得一提的是 error-first 回调风格(nodejs设计了大量此风格的API)

  • Promise常见的三个术语:决议(resolve)、完成(fulfill)和拒绝(reject)

  • Promise.resolve(obj) == obj 可以用来检测是obj否为Promise对象

  • Promise对象的then方法会自动创建一个新的 Promise 从调用返回(相当于将then函数的返回值用Promise.resolve过滤了下),可以无限链式调用下去

  • Promise.prototype.finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的