Promise
基础知识
函数对象、实例对象
- 函数对象
- 将函数作为对象使用时,简称为函数对象
- 实例对象
- new 构造函数或类产生的对象,称为实例对象
回调函数
- 作为参数传递的函数
- 函数定义了,但没有主动调用,最终却执行的函数
回调函数分类
- 同步的回调函数
- 立即在主线程上执行,不会放入 回调队列 中
- 例子:数组遍历相关的回调函数
- 异步的回调函数
- 不会立即执行,会放入 回调队列 中以后执行
- 例子:定时器回调 / ajax 回调
错误和处理
错误类型
- Error:所有错误的父类型
- ReferenceError:引用变量不存在
- TypeError:数据类型不正确
- RangeError:数据值不在其所允许的范围内-死循环
- SyntaxError:语法错误
错误处理
- 捕获错误:try{…}catch(){…}
- 抛出错误:throw new Error(“错误描述”)
错误信息
- message属性:错误相关信息
- stack属性:记录信息
Promise
理解promise
抽象表达
Promise 是 JS 中进行异步编程的新方案。旧方案-纯粹的回调
具体表达
- 语法上:Promise 是一个内置 构造函数
- 功能上:Promise 实例对象可以封装一个异步操作,并获得成功/失败值
Promise 说明
- Promise 是构造函数
- new Promise 需要传入回调函数
- 是同步的回调函数(不会放入回调队列)
- 立即在主线程上执行
- 称为:executor 函数(执行器函数)
- Promise 有 3 种状态
- 初始化(pending)
- 成功(fulfilled)
- 失败(rejected)
- Promise 实例被 new 出来时,都是初始化(pending)状态
- executor 函数(执行器函数)接收 2 个形参
- resolve:调用 resolve,会让 Promise实例状态变为:成功(fulfilled),同时可以传入成功值 value
- reject:调用 reject,会让 Promise实例状态变为:失败(rejected),同时可以传入失败的 reason
基本使用流程
- 创建 Promise 实例对象,传入 executor 函数
- executor 函数中执行异步任务
- 根据异步任务结果,改变 Promise 实例状态
- 异步任务成功:
- 调用 resolve(value) ,Promise 实例状态变为成功(fulfilled),同时指定成功的 value
- 异步任务失败:
- 调用 reject(reason) ,Promise 实例状态变为失败(rejected),同时指定失败的 reason
- 异步任务成功:
- 通过 then 方法为 Promise 的实例指定成功、失败的回调函数,来获取成功的 value,失败的 reason
- then 方法指定的:成功的回调,失败的回调,都是异步的回调
1 | |
注意事项
- 如何改变状态
- pending:未确定的——初始状态
- fulfilled:成功的——调用 resolve() 后的状态
- rejected:失败的——调用 reject() 后的状态
- 状态只能改变一次
- pending ==> fulfilled
- pending ==> rejected
- 多次调用 then 为 Promise 实例定义多个成功/失败回调,最终会按照调用顺序依次执行(都放入了回调队列中)
注意事项
executor 抛异常
- executor 抛出异常,会将 Promise 实例的 pending 状态的 改为 rejected 状态
- 在调用 resolve() 之后抛出异常,则 Promise 状态不发生改变,依旧是 fulfilled 状态
回调执行时机
回调函数:then() 中定义的回调函数
状态改变:Promise 的状态
回调执行条件
- 回调函数定义
- Promise 状态发生改变(非 fulfilled)
回调执行时机
- 先指定的回调,当状态发生改变时,回调函数就会执行
- then 方法执行后,成功/失败回调都会存在 Promise 实例 的一个数组中
- 当 状态改变,调用成功或失败回调时,将 Promise 实例上的对应回调,先存入 微队列,主线程任务执行完毕,然后再依次调用 微任务(调用 宏任务 前)
- 先改变的状态,当指定了回调时,回调函数就会执行
- 状态先改变,当 执行 then 方法后,成功/失败回调不会存储在 Promise 实例上了,而是 直接 将对应回调先存放到 微队列,主线程任务执行完毕,然后再依次调用 微任务(调用 宏任务 前)
then的链式调用
- then() 返回的是一个 新的 Promise 实例
- 状态和值 由执行的 回调函数返回值决定
- 返回非 Promise 数据类型
- 新 Promise 实例 状态为成功(fulfilled),值为回调函数返回值
- 包括没定义返回值的 undefined
- 返回 Promise 实例
- 新 Promise 实例 状态和值,与回调函数返回的 Promise 实例一致
- 回调函数抛出异常
- 新 Promise 实例 状态为失败 rejected ,reason 为抛出的那个异常
- 返回非 Promise 数据类型
中断 then 链式调用
then() 回调函数返回值,返回一个 pending 状态的 Promise 实例
1 | |
错误穿透
- 可以只定义一个失败回调,统一处理 then 链式调用任何阶段出现的失败状态
- then 定义失败回调之前,其他的 then 中定义的回调只能是成功回调
原理
因为 then 没有定义失败回调,当 Promise 实例的状态是失败 rejected ,需要调用失败回调 reject 时会抛出异常,所以当前的 then 的返回值是一个失败状态的 Promise ,以此类推最后一个 then 定义的失败回调就会被调用
1 | |
Promise 方法
Promise.prototype
- 原型链上定义的方法
- Promise 实例调用的方法
then
p.then(onFulfilled,onRejected)
- onFulfilled:成功的回调函数 (value) => {}
- onRejected:失败的回调函数 (reason) => {}
注意事项
- then 方法返回一个新的 Promise 实例对象
- onFulfilled 可以用任意类型值进行占位,当 Promise 状态为成功不进行任何操作
- onRejected 如果省略了,当 Promise 状态为失败,则会报错没有对失败进行处理(底层定义了onRejected 回调,只是函数体是抛出了异常)
catch
p.catch(onRejected)
catch 方法是 then 方法的语法糖,相当于 then(undefined,onRejected)
- onRejected:失败的回调函数 (reason) => {}
Promise
- Promise 函数对象上定义的方法
- Promise 函数对象调用的方法
resolve
Promise.resolve(value)
快速返回一个状态为成功(fulfilled)的 Promise 对象
注意事项
- 如果 value 为非 Promise 对象,返回一个状态为成功(fulfilled)的 Promise 对象
- 如果 value 为 Promise 对象,则返回此 Promise 对象 (p1 == p2)
reject
Promise.reject(reason)
快速返回一个状态为失败(rejected)的 Promise 对象
注意事项
- 不论传入的 reason 是何种类型值(包括 Promise 实例),返回的都是一个 新的 ,失败的 Promise 实例
- 传入 Promise 实例,返回的失败 Promise 实例的状态和 reason 由参数决定
all
Promise.all(promiseArr)
promiseArr:n 个 Promise 实例组成的数组
- 返回一个新的 Promise 实例
- 数组中所有 Promise 实例都成功才返回一个 新的 成功 promise 实例
- 只要有一个失败,则返回一个新的失败的 Promise 实例
- 成功 Promise 实例:数组中所有成功 Promise 实例的 value 组成的数组
- 失败 Promise 实例:数组中第一个失败的 Promise 实例的 reason
race
Promise.race(promiseArr)
promiseArr:n 个 Promise 实例组成的数组
- 返回一个 新的 Promise 实例,与最先出结果的 Promise 实例状态和值相同
async和await
用于优化 Promise 链式调用
async
- async 用来修饰函数(使用 await 的必要条件)
- 修饰的函数返回值是一个 Promise 对象
- 返回的 Promise 实例的状态和值由函数的返回值决定 原理等同于then的链式调用
await
用于等待接收一个将 Promise 实例转换为成功状态时传入的 value
- await 右侧的表达式一般是 Promise 实例对象,但也可以是其他类型数据
- Promise 对象:await表达式的返回值是 Promise 成功状态的值(只接收成功值)
- 其他类型数据:直接作为 await 的返回值
用法
- await 必须写在 async 修饰的函数中,但 async 函数中可以没有 await
- 如果 await 右侧的 Promise 实例是失败状态,就会抛出异常,需要通过 try..catch(){…} 进行捕获处理
1 | |
原理
- await 表达式在底层转换成只定义了成功回调的 then 方法
- await 之后的所有代码,作为成功回调的内容
- 变量作为接收成功回调的形参
- async 函数是立即执行,但是里面代码会在回调队列中等待执行
1 | |
宏队列和微队列
执行异步回调函数时,都先放在队列中,当 主线程任务执行完毕后 ,然后从队列中进行调用,JS中的队列分为宏队列和微队列
- 宏队列:[宏任务1,宏任务2,宏任务3…]
- 宏队列中存放宏任务
- 微队列:[微任务1,微任务2,微任务3…]
- 微队列中存放微任务
队列存放任务规则
当异步函数执行时,才先放入队列中(此时没有执行),主线程(微队列)任务执行完毕,然后从队列调用
执行规则
每次要执行宏队列中的任何任务之前,先看微队列中是否有任务(先执行微队列任务)
- 如果有,依次执行微队列中任务
- 如果没有,按照宏队列任务依次执行
区分
目前 JS 中微任务只有 Promise,其他异步任务都是宏任务
