基础知识

函数对象、实例对象

  1. 函数对象
    • 将函数作为对象使用时,简称为函数对象
  2. 实例对象
    • new 构造函数或类产生的对象,称为实例对象

回调函数

  1. 作为参数传递的函数
  2. 函数定义了,但没有主动调用,最终却执行的函数

回调函数分类

  1. 同步的回调函数
    • 立即在主线程上执行,不会放入 回调队列
    • 例子:数组遍历相关的回调函数
  2. 异步的回调函数
    • 不会立即执行,会放入 回调队列 中以后执行
    • 例子:定时器回调 / ajax 回调

错误和处理

错误类型

  1. Error:所有错误的父类型
  2. ReferenceError:引用变量不存在
  3. TypeError:数据类型不正确
  4. RangeError:数据值不在其所允许的范围内-死循环
  5. SyntaxError:语法错误

错误处理

  1. 捕获错误:try{…}catch(){…}
  2. 抛出错误:throw new Error(“错误描述”)

错误信息

  1. message属性:错误相关信息
  2. stack属性:记录信息

Promise


理解promise

抽象表达

Promise 是 JS 中进行异步编程的新方案。旧方案-纯粹的回调

具体表达

  1. 语法上:Promise 是一个内置 构造函数
  2. 功能上:Promise 实例对象可以封装一个异步操作,并获得成功/失败值

Promise 说明

  1. Promise 是构造函数
  2. new Promise 需要传入回调函数
    1. 是同步的回调函数(不会放入回调队列)
    2. 立即在主线程上执行
    3. 称为:executor 函数(执行器函数)
  3. Promise 有 3 种状态
    1. 初始化(pending
    2. 成功(fulfilled
    3. 失败(rejected
  4. Promise 实例被 new 出来时,都是初始化(pending)状态
  5. executor 函数(执行器函数)接收 2 个形参
    1. resolve:调用 resolve,会让 Promise实例状态变为:成功(fulfilled),同时可以传入成功值 value
    2. reject:调用 reject,会让 Promise实例状态变为:失败(rejected),同时可以传入失败的 reason

基本使用流程

  1. 创建 Promise 实例对象,传入 executor 函数
  2. executor 函数中执行异步任务
  3. 根据异步任务结果,改变 Promise 实例状态
    1. 异步任务成功:
      • 调用 resolve(value) ,Promise 实例状态变为成功(fulfilled),同时指定成功的 value
    2. 异步任务失败:
      • 调用 reject(reason) ,Promise 实例状态变为失败(rejected),同时指定失败的 reason
  4. 通过 then 方法为 Promise 的实例指定成功、失败的回调函数,来获取成功的 value,失败的 reason
    • then 方法指定的:成功的回调,失败的回调,都是异步的回调
1
2
3
4
5
6
7
8
9
10
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("我是成功的数据")
})
})

p.then(
(value)=>{ console.log("成功回调",value) },
(reason)=>{ console.log("失败回调",reason) }
)

注意事项

  1. 如何改变状态
    1. pending:未确定的——初始状态
    2. fulfilled:成功的——调用 resolve() 后的状态
    3. rejected:失败的——调用 reject() 后的状态
  2. 状态只能改变一次
    1. pending ==> fulfilled
    2. pending ==> rejected
  3. 多次调用 then 为 Promise 实例定义多个成功/失败回调,最终会按照调用顺序依次执行(都放入了回调队列中)

注意事项


executor 抛异常

  1. executor 抛出异常,会将 Promise 实例的 pending 状态的 改为 rejected 状态
  2. 在调用 resolve() 之后抛出异常,则 Promise 状态不发生改变,依旧是 fulfilled 状态

回调执行时机

回调函数:then() 中定义的回调函数
状态改变:Promise 的状态

回调执行条件

  1. 回调函数定义
  2. Promise 状态发生改变(非 fulfilled)

回调执行时机

  1. 先指定的回调,当状态发生改变时,回调函数就会执行
    1. then 方法执行后,成功/失败回调都会存在 Promise 实例 的一个数组中
    2. 状态改变,调用成功或失败回调时,将 Promise 实例上的对应回调,先存入 微队列,主线程任务执行完毕,然后再依次调用 微任务(调用 宏任务 前)
  2. 先改变的状态,当指定了回调时,回调函数就会执行
    1. 状态先改变,当 执行 then 方法后,成功/失败回调不会存储在 Promise 实例上了,而是 直接 将对应回调先存放到 微队列,主线程任务执行完毕,然后再依次调用 微任务(调用 宏任务 前)

then的链式调用

  • then() 返回的是一个 新的 Promise 实例
  • 状态和值 由执行的 回调函数返回值决定
    1. 返回非 Promise 数据类型
      • 新 Promise 实例 状态为成功(fulfilled),值为回调函数返回值
      • 包括没定义返回值的 undefined
    2. 返回 Promise 实例
      • 新 Promise 实例 状态和值,与回调函数返回的 Promise 实例一致
    3. 回调函数抛出异常
      • 新 Promise 实例 状态为失败 rejected ,reason 为抛出的那个异常

中断 then 链式调用

then() 回调函数返回值,返回一个 pending 状态的 Promise 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var p = new Promise((resolve,reject)=>{
resolve(); //状态改为 fulfilled
})

p.then(
(value)=>{
console.log("成功1")
return new Promise(()=>{}) //返回一个 pending 状态的 Promise 实例,就不会继续执行后面成功或失败的回调函数
},
(reason)=>{
console.log("失败1")
}
)
.then(
(value)=>{
console.log("成功2")
},
(reason)=>{
console.log("失败2")
}
)

错误穿透

  1. 可以只定义一个失败回调,统一处理 then 链式调用任何阶段出现的失败状态
  2. then 定义失败回调之前,其他的 then 中定义的回调只能是成功回调

原理

因为 then 没有定义失败回调,当 Promise 实例的状态是失败 rejected ,需要调用失败回调 reject 时会抛出异常,所以当前的 then 的返回值是一个失败状态的 Promise ,以此类推最后一个 then 定义的失败回调就会被调用

1
2
3
4
5
6
7
var p = new Promise((resolve,reject)=>{
reject();
})
p.then(()=>{console.log("成功1")})
.then(()=>{console.log("成功2")})
.then(()=>{console.log("成功3")})
.catch(()=>{console.log("处理之前出现的失败回调")}) //之前 Promise 实例出现失败状态,在此进行统一处理

Promise 方法


Promise.prototype

  1. 原型链上定义的方法
  2. Promise 实例调用的方法

then

p.then(onFulfilled,onRejected)

  1. onFulfilled:成功的回调函数 (value) => {}
  2. onRejected:失败的回调函数 (reason) => {}

注意事项

  1. then 方法返回一个新的 Promise 实例对象
  2. onFulfilled 可以用任意类型值进行占位,当 Promise 状态为成功不进行任何操作
  3. onRejected 如果省略了,当 Promise 状态为失败,则会报错没有对失败进行处理(底层定义了onRejected 回调,只是函数体是抛出了异常)

catch

p.catch(onRejected)

catch 方法是 then 方法的语法糖,相当于 then(undefined,onRejected)

  1. onRejected:失败的回调函数 (reason) => {}

Promise

  1. Promise 函数对象上定义的方法
  2. Promise 函数对象调用的方法

resolve

Promise.resolve(value)

快速返回一个状态为成功(fulfilled)的 Promise 对象

注意事项

  1. 如果 value 为非 Promise 对象,返回一个状态为成功(fulfilled)的 Promise 对象
  2. 如果 value 为 Promise 对象,则返回此 Promise 对象 (p1 == p2

reject

Promise.reject(reason)

快速返回一个状态为失败(rejected)的 Promise 对象

注意事项

  1. 不论传入的 reason 是何种类型值(包括 Promise 实例),返回的都是一个 新的 ,失败的 Promise 实例
  2. 传入 Promise 实例,返回的失败 Promise 实例的状态和 reason 由参数决定

all

Promise.all(promiseArr)

promiseArr:n 个 Promise 实例组成的数组

  1. 返回一个新的 Promise 实例
    1. 数组中所有 Promise 实例都成功才返回一个 新的 成功 promise 实例
    2. 只要有一个失败,则返回一个新的失败的 Promise 实例
  2. 成功 Promise 实例:数组中所有成功 Promise 实例的 value 组成的数组
  3. 失败 Promise 实例:数组中第一个失败的 Promise 实例的 reason

race

Promise.race(promiseArr)

promiseArr:n 个 Promise 实例组成的数组

  1. 返回一个 新的 Promise 实例,与最先出结果的 Promise 实例状态和值相同

async和await

用于优化 Promise 链式调用

async

  1. async 用来修饰函数(使用 await 的必要条件)
  2. 修饰的函数返回值是一个 Promise 对象
  3. 返回的 Promise 实例的状态和值由函数的返回值决定 原理等同于then的链式调用

await

用于等待接收一个将 Promise 实例转换为成功状态时传入的 value

  • await 右侧的表达式一般是 Promise 实例对象,但也可以是其他类型数据
    1. Promise 对象:await表达式的返回值是 Promise 成功状态的值(只接收成功值)
    2. 其他类型数据:直接作为 await 的返回值

用法

  1. await 必须写在 async 修饰的函数中,但 async 函数中可以没有 await
  2. 如果 await 右侧的 Promise 实例是失败状态,就会抛出异常,需要通过 try..catch(){…} 进行捕获处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var p = new Promise((resolve,reject)=>{
resolve("数值");
})
var p2 = new Promise((resolve,reject)=>{
resolve("数值2");
})

;(async ()=>{ // 前面的分号; 用于以防代码不规范引起的报错,将自运行函数与前面代码链接起来
try{
var v = await p;
console.log(v) // 输出内容:数值
var v2 = await p2;
console.log(v2) //输出内容:数值2
}catch(error){
console.log(error) //获取到失败状态回调的值
}
}){}

原理

  1. await 表达式在底层转换成只定义了成功回调的 then 方法
  2. await 之后的所有代码,作为成功回调的内容
  3. 变量作为接收成功回调的形参
  4. async 函数是立即执行,但是里面代码会在回调队列中等待执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var p = new Promise((resolve,reject)=>{
resolve("数值");
})

;(async()=>{
var vv = await p;
console.log(111)
console.log(222)
console.log(333)

/*---------------------转换结果

p.then( // Promise 实例调用 then
(vv)=>{ // 变量作为接收形参
console.log(111) // await 之后代码作为成功回调函数体
console.log(222)
console.log(333)
}
)

*/
})()

宏队列和微队列

执行异步回调函数时,都先放在队列中,当 主线程任务执行完毕后 ,然后从队列中进行调用,JS中的队列分为宏队列和微队列

  1. 宏队列:[宏任务1,宏任务2,宏任务3…]
    • 宏队列中存放宏任务
  2. 微队列:[微任务1,微任务2,微任务3…]
    • 微队列中存放微任务

队列存放任务规则

当异步函数执行时,才先放入队列中(此时没有执行),主线程(微队列)任务执行完毕,然后从队列调用

执行规则

每次要执行宏队列中的任何任务之前,先看微队列中是否有任务(先执行微队列任务

  1. 如果有,依次执行微队列中任务
  2. 如果没有,按照宏队列任务依次执行

区分

目前 JS 中微任务只有 Promise,其他异步任务都是宏任务