本文主要讲解 Promise 的常用方法以及使用技巧,并实现如何手写一个 Promise。
Promise 的出现
回调地狱问题
在传统的 JS 编程中始终存在一个问题,即当当前回调函数的执行依赖于上一个回调函数的执行结果的时候,会形成回调函数层层嵌套的问题,严重影响代码的可读性与可维护性,这种现象一般称之为回调地狱。
下面为示例代码,回调地狱的一个比较常见的情景为ajax
请求,即下一个请求的是否发起依赖于上一个请求的结果。
Promise 的出现
Promise
是什么?其解决了什么问题?
Promise
是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。ES6 将其写进了语言标准,统一了用法,原生提供了 Promise
对象。指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs 模块、ajax、数据库操作。从语法上说,Promise
是一个构造函数;从功能上说,Promise
对象用来封装一个异步操作并可以获取其成功/失败的结果值。
Promise
设计的核心理念是什么?
- 从
Promise
的角度来说,是将状态改变与回调函数彻底区分开。
- 从应用
Promise
的角度来说,以ajax
请求数据为例,则是将数据请求与数据处理区分开。
Promise 的实际应用
案例 1:利用 promise 来进行读取文件操作
案例 2:利用 promise 进行 ajax 请求
案例 3:利用 promise 进行数据库操作
案例 4:封装一个函数,作用是读取文件
node
中的 promisify
promisify
(只能在 NodeJS
环境中使用)
promisify
是 util
模块中的一个方法 util
是 nodeJS
的内置模块
- 作用: 返回一个新的函数, 函数是
promise
风格的.
查看 promisify 的手写实现
看到 promisify 这个函数对其内部实现机制比较感兴趣,那我们就来手写一下。
首先promisify
函数得返回一个函数,同时返回的这个函数应该返回一个Promise
对象,根据这两点我们把函数的基本结构搭建起来。
利用返回的这个Promise
对象,可以使用then
方法进行回调的处理,即原来函数的回调决定这Promise
状态的改变以及执行,所以需要将改变Promise
对象的状态的回调函数传入参数中。
Promise 的实例方法
then
注意点:通过then
返回的promise
的对象的状态由谁决定。
- 若返回非
promise
对象或者什么都不返回则,状态为fullfilled
。
- 若函数执行过程中抛出错误,则状态为
rejected
。
- 若返回一个
promise
对象,则状态与返回的promise
对象保持一致。
catch
能够穿透捕获Promise
中的错误,其实相当于Promise.prototype.then(undefined, onRejected)
。
finally
finally
是在 ES9(ES2018)中新增的一个特性:表示无论 Promise
对象变成 fufilled
还是 rejected
状态,最终都会被执行。
finally
方法中的回调函数是不接受参数的,因为无论前面是 fulfilled
状态还是 rejected
状态, 它都是执行。
Promise 类静态方法
resolve
- 当参数不是
Promise
对象的时候,返回promise
状态为fullfilled
。
- 当参数是
Promise
对象的时候,返回的Promise
实例的属性由该Promise
实例决定。
reject
始终返回状态为rejected
的Promise
对象
all
接受一个Promise
对象的数组,如果数组中的Promise
对象的状态都是fullfilled
,则返回的也是一个状态为fullfilled
对象,PromiseResult
的值为每个成功值的数组。若存在状态为rejected
的对象则返回一个状态为rejected
的Promise
对象,PromiseResult
为第一个状态为rejected
的Promise
对象。
race
和all
方法类似都是接受一个Promise
对象的数组,但其状态与结果由数组中最先改变的Promise
对象决定。
allSettled
与all
方法类似,当数组内所有promise
的状态都确定后执行成功的回调。基本上只有成功的回调。
any
与all
方法正好相反,只要参数实例有一个变成 fulfilled
状态,包装实例就会变成 fulfiilled
状态;
如果所有参数实例都变成 rejected
,包装实例就会变成 rejected
状态,结果为一个AggregateError
对象。
关键问题
中断 Promise 链
终止Promise
链条,有且仅有一种方式,即返回一个状态为pending
的promise
对象。
修改 Promise 的状态
Promise 串联多个任务
执行顺序分析
使用 JS 手写一个 promise
由于注释写的比较清除就不分步写了,注意Promise
类的静态方法race
等,接受的是一个可迭代对象,同时注意在写的时候所用到的技巧,foreach
以及数组的原生方法。
值得注意的地方有:
_resolve
的单独处理,处理嵌套的Promise
对象。
- 静态方法接受可迭代对象的方法大致相同,但注意空数组的时候返回什么。
- 注意原生实现的时候
then
方法不能使用箭头函数定义,会出现this
指向无法指向实例对象的问题。
原生实现
class 实现
原生的都写出来了只能说class
版本有手就行,注意class field
写法的方法仍然属于实例属性,class field
的主要设计目的就是为了解决在类里面使用this
的种种不便,精简了部分代码。