0%

学习笔记 2020 10 29

学习笔记 2020-10-29

JavaScript 高级程序设计(第4版) 阅读记录

期约与异步函数

期约

ECMAScript 6 增加了对 Promises/A+ 规范的完善支持,即 Promise 类型。

创建 Promise 时需要传入执行器 ( executor ) 函数作为参数。

期约基础
  1. 期约状态机

    在把一个期约实例传给 console.log() 时,控制台输出表明该实例处于待定状态。期约是一个有状态的对象,可能处于如下 3 种状态之一:

    • 待定 ( pending )
    • 兑现 ( fulfilled ,有时候也成为 解决 , resolved )
    • 拒绝 ( rejected )

    待定是期约的最初始状态。在待定状态下,期约可以落定( settled )为代表成功的兑现状态,或者代表失败的拒绝状态。落定的行为是不可逆的。落定后,契约的状态不再改变。期约也可能永远处于待定状态。

    期约的状态是私有的,无法直接通过 JavaScript 检测到。为了避免根据读取到的期约状态,以同步方式处理期约对象。期约的状态也不能被外部 JavaScript 代码修改。

  2. 解决值、拒绝理由及期约用例

    期约有两大用途。

    首先是抽象地表示一个异步操作。根据期约的状态可以得知一段异步代码是否已经完成。

    在另外一些情况下,期约封装的异步操作会实际生成某个值。程序可以根据期约状态改变来访问这个值或是拒绝理由。

    每个期约只要状态切换为兑现,就会有一个私有的内部值。只要状态切换为拒绝,就会有一个私有的内部理由。二者都是可选的,默认值为 undefined 。期约到达某个落定状态时执行的异步代码始终会收到这个值或理由。

  3. 通过执行函数控制期约状态

    期约的状态是私有的,在执行器函数内部进行操作。

    执行器函数有两项职责:

    • 初始化期约的异步行为。
    • 控制状态的最终转换。

    控制期约状态的转换是通过调用它的两个函数参数实现的。即 resolve()reject() 。调用前者会把状态切换为兑现,调用后者会把状态切换为拒绝。

  4. Promise.resolve()

    可以通过调用 Promise.resolve() 静态方法实例化一个解决的期约。这个解决的期约的值对应着传入的第一个参数,其余参数会被忽略。这个方法可以包装任何非期约值,包括错误对象,将其转换为解决的期约。

  5. Promise.reject()

    Promise.reject() 会实例化一个拒绝的期约并抛出一个异步错误。这个错误只能通过拒绝处理程序捕获。

    Promise.resolve() 会接收期约参数的值来作为返回值的值。但 Promise.reject 不会把期约参数的值来传递,而是直接把这个期约对象作为拒绝的理由。

  6. 同步/异步执行的二元性

    try {
      throw new Error('foo');
    } catch (e) {
      console.log(e); // Error: foo
    }
    try {
      Promise.reject(new Error('bar'));
    } catch (e) {
      console.log(e);
    }
    // Uncaught (in promise) Error: bar

    此处期约抛出的错误无法被同步代码所捕获。因为期约对象需要通过异步模式捕获错误。期约是同步对象,但是异步执行模式的媒介。

    拒绝期约的错误并没有抛到执行同步代码的线程里,而是通过浏览器异步消息队列来处理的。因此只能使用异步结构与之交互。

期约的实例方法

期约实例的方法是连接外部同步代码与内部异步代码的桥梁。这些方法可以访问异步操作返回的数据,处理期约成功和失败的结果,连接对期约求值,或者添加只有期约进入终止状态时才会执行的代码。

  1. 实现 Thenable 接口

    在 ECMAScript 暴露的异步结构中,任何对象都有一个 then() 方法。这个方法被认为实现了 Thenable 接口。

    ECMAScript 的 Promise 类型实现了 Thenable 接口。

  2. Promise.prototype.then()

    Promise.prototype.then() 是为期约实例添加处理程序的主要方法。接收最多两个参数: onResolved 处理程序和 onRejected 处理程序。两个参数都是可选的,提供的时候会在期约分别进入兑现和拒绝状态时执行。任何非函数参数都会被忽略。该方法返回一个新的期约实例。会通过 Promise.resolve() 包装生成新期约,没有提供处理程序的话,会包装上一个期约解决后的值。没有显示指定返回语句的话,会包装默认返回值 undefined

    onRejected 处理程序返回值会被 Promise.resolve() 包装。

  3. Promise.prototype.catch()

    相当于调用 Promise.prototype.then(null, onRejected)

  4. Promise.prototype.finally()

    这个处理程序在期约转换为解决或拒绝状态时都会执行。但无法知道期约的状态,一般用于添加清理代码。该方法返回一个新的期约实例。不同于 then() 返回的实例,被设计为一个状态无关的方法。在大多数情况下它将表现为父期约的传递。

现代 JavaScript 教程

对象—原始值转换

所有对象在布尔上下文中均为 true

数值转换发生在对象相减或应用数学函数时。

字符串转换发生在输出对象和类似的上下文中。

为了进行转换, JavaScript 尝试查找并调用三个对象方法:

  1. 调用 obj[Symbol.toPrimitive]
  2. 如果上述方法不存在,且进行字符串转换,尝试 obj.toString()obj.valueOf()
  3. 如果上述方法不存在,且进行数字转换或是默认转换,尝试 obj.valueOf()obj.toString()

toString 方法返回一个字符串 "[object Object]"

valueOf 方法返回对象本身。

三个方法唯一的限制是返回一个原始值而不是对象。