Promise 知识点总结
Promise 是 JavaScript 中用于处理异步操作的一种机制。它是一个对象,表示某个异步操作的最终结果 ,成功或失败 ;
Promise 的三种状态
(1) 对象的状态不受外界影响
Promise
对象通过自身的状态 ,来控制异步操作。它有三种状态
pending
: 初始状态(进行中)fulfilled
: 操作成功,它调用.then
回调,例如.then(onSuccess)
。rejected
: 操作失败,它调用.catch
或.then
的第二个参数(如果有)
注:fulfilled
和rejected
合在一起称为resolved
(已定型)
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2) 状态一旦发生改变就不会再变
Promise
的英文名称叫承诺 , 承诺一旦生效 , 将不在发生改变
这三种的状态的变化途径只有两种 :
- 从“未完成” 到 “成功”
- 从“未完成” 到 “失败”
new Promise(callback)
Promise
是一个对象,也是一个构造函数
- 通常使用
new Promise
构造函数创建一个Promise
对象 , 需要传入一个回调函数callback
作为参数 , 该回调函数会立即执行。 new Promise
执行的时候会给callback
函数传入两个参数resolve
和reject
// 声明一个新的Promise对象,他的初始状态为pending
new Promise((resolve, reject) => {
console.log("Promise 执行了"); // 立即执行
});
resolve 和 reject 函数
这两个函数用于改变 Promise
的状态
resolve
在异步操作成功时调用 , 将Promise
的状态从"未完成"变为"成功" (即从pending
变成fulfilled
)reject
在异步操作失败时调用 , 将Promise
的状态从"未完成"变为"失败" (即从pending
变成rejected
)
异步操作的结果 , 是作为参数传递出去的, 由 .then
或 .catch
接收
可以使用简写 Promise.resolve
或者 Promise.reject
创建一个成功或者失败的 Promise
对象
const p_1 = new Promise((resolve, reject) => {
return resolve("成功了");
// return reject("失败了");
});
Promise.resolve("start").then(() => {}); // 简写
then 函数
-
then(success, failed)
方法用于处理异步操作成功的情况 , 它接受两个参数:success
是成功的回调函数 ,failed
是失败的回调函数 -
success
接收resolve
传入的值作为参数 ,failed
接收reject
传入的值作为参数new Promise((resolve, reject) => { return resolve("成功了"); // return reject("失败了"); }).then( (res) => { console.log(res); // resolve触发 }, (res) => { console.log(res); // reject触发 | .then的第二参数可以省略,在开发中不常用 } );
-
Promise
的 每个.then
方法都会返回一个新的Promise
对象 , 可以then
之后还能继续调用then
, 实现 链式调用 , 同样的.catch
也可以链式调用 -
then()
或者catch()
的参数期望是函数 , 传入非函数则会发生值透传new Promise((resolve, reject) => { resolve("start"); }) // .then(1) // 非函数,值透传 | - 不常用 .then((res) => { console.log(res); });
返回值处理
在 then
的回调函数内可以return值
或 抛出错误
, 我们需要对这些返回值进行处理
- 返回自身 => 报错
- 返回已知状态的
Promise
对象 => 状态由返回的Promise
对象决定 - 没有 return , 相当于
return Promise.resolve(undefined)
- return 任意值 ,【不包含
Promise.reject
和(throw new Error)
】, 会被包装成一个状态为fulfilled
的Promise
对象 ,相当于resolve(任意值)
throw new Error
抛出错误 ,Promise
的值状态变成rejected
, 相当于reject(错误)
let p = new Promise((resolve, reject) => {
resolve("成功获取到数据");
})
.then(() => {
// return p; //返回自身 => 报错
return Promise.resolve(9999); //返回已知状态的Promise对象
})
.then((res) => {
console.log("then1", res);
return 11; // 相当于 return Promise.resolve(11)
// return new Error("error-1"); // 这句也是返回任意值 ***
})
.then((res) => {
console.log("then2", res); //相当于 return Promise.resolve(undefined)
});
catch 函数
catch()
方法用于捕获异步操作失败的情况 ,它接收一个回调函数作为参数 , 用来打印错误信息。
- 当
Promise
被reject
时会被catch
捕获 - 在
Promise
链中的then/finall
回调中抛出的异常会被catch
捕获
new Promise((resolve, reject) => {
resolve(1);
})
.then((res) => {
return new Error("error-1"); // 没抛错 , 进then
})
.then((res) => {
console.log("then2", res);
throw new Error("error-2"); // throw抛错 , 进catch
})
.catch((err) => {
console.log("catch", err);
});
分析 :
- Promise 新建后就会立即执行,
resolve(1)
使 Promise 状态变为fulfilled
- 执行第一个
then
, 注意return new Error === Promise.resolve(new Error)
, 所以会进入.then
而不是catch
- 执行第二个
then
, 输出 "then2,error-2" ; 此处使用 throw 抛出错误 ,throw new Error === return Promise.reject
catch
捕获异常 , 输出 "catch Error : error-2"
finally 函数
- 无论
Promise
成功或失败 ,finally
的回调函数都会在当前Promise
完成后立即执行 , 而不是在链的末尾 finally
不接收前一个Promise
的结果 , 通常不需参数- 无异常时 ,
finally
返回值不影响Promise
链 ; 若抛出错误 , 将由最近的catch
捕获 finally
专用于执行与结果无关的收尾操作 , 不涉及值的传递 , 如果需要处理Promise
的结果 , 应该在then
或catch
中进行
Promise.resolve("start")
.finally(() => {
console.log("1"); // 1
return "finally";
})
.then((res2) => {
console.log("then", res2); // then start
})
.finally(() => {
throw new Error("error");
})
.catch((res3) => {
console.log("catch", res3); // catch error
});
Promise 并发
Promise 类提供了四个静态方法来促进异步任务的并发 :
Promise.all()
: 在所有传入的 Promise 都被兑现时兑现 ;在任意一个 Promise 被拒绝时拒绝。Promise.allSettled()
: 在所有的 Promise 都被敲定时兑现。Promise.any()
: 在任意一个 Promise 被兑现时兑现;仅在所有的 Promise 都被拒绝时才会拒绝。Promise.race()
: 在任意一个 Promise 被兑现时兑现;在任意一个的 Promise 被拒绝时拒绝。
async/await
async/await
语法用于处理异步操作 , async
关键字用于声明一个函数是异步的,await
关键字用于等待一个 Promise
的结果
async function funC_3() {
let value_2 = await new Promise((resolve, reject) => {
resolve("成功获取到数据");
});
console.log(value_2);
}
funC_3();
解析 : 给 value_2 赋值、输出 value_2 , 是在微任务队列中的
Promise.resolve().then(() => {
new Promise((resolve, reject) => {
resolve("成功获取到数据");
}).then((value_2) => {
console.log(value_2);
});
});
这两种写法是等效的 , async/await 减少了嵌套 , 提高了代码的可读性和维护性,更推荐这种写法
总结 : 可以使用 then
方法接收异步操作成功的返回值 , 也可以使用 async/await
接收异步操作成功的返回值
js 任务调度
同步任务 : 在主线程上按顺序执行,必须等待前一个任务完成才能开始下一个。
异步任务 : 不直接在主线程上执行,而是先放入"任务队列"。只有当主线程空闲时,才会从任务队列中取出异步任务放到主线程中去执行 , 异步任务分为宏任务
和微任务
。
宏任务
包含整个脚本的初始执行、事件(如点击、键盘输入)、setTimeout
、setInterval
、I/O、UI 渲染等 , 这些任务在事件循环的不同阶段执行。
微任务
是更为轻量级的任务,它们在当前宏任务结束后,但在下一个宏任务开始之前执行。典型的微任务包括:
Promise
的 .then
和.catch
等 、async/await
的后续操作 , Ajax请求
、MutationObserver
、 process.nextTick
( Node.js 环境特有)
JavaScript 事件循环 (Event Loop) 依次执行以下步骤:
- 主任务执行
- 初始化执行上下文 :
- 首次执行全局上下文中的代码 , 这是一个同步过程
- 在这个阶段 , 变量声明、函数声明等被处理
- 队列准备 :
- 在执行过程中 , 如果遇到异步操作(如 setTimeout、Promise 等) , 相关任务会被添加到宏任务队列或微任务队列。
- 宏任务队列示例 : Macro = [] , (此处用数组模拟队列做展示)
- 微任务队列示例 : Micro = []
- 同步代码执行结束后 , 当主线程空闲时 , 优选处理微任务
- 事件循环开始
-
微任务调度 :
- 宏任务执行完毕 , 立即 清空微任务队列
- 执行过程中新产生的宏任务和微任务 , 分别加入各自的队列
- 注意 :本轮执行过程中新添加的微任务也会在本轮清空过程中执行
-
宏任务调度 :
- 微任务执行完毕后 , 从宏任务队列中 取出一个宏任务执行
- 执行过程中新产生的宏任务和微任务 , 分别加入各自的队列
- 每执行完一个宏任务 , 就需要去清空微任务队列
- 循环迭代
- 重复【微任务调度 - 宏任务调度】的过程 , 直到两个队列均为空 , 循环结束
案例一:
console.log("start!");
setTimeout(() => {
console.log("TimeOut!");
}, 0);
Promise.resolve("Promise!").then((res) => {
console.log("res!");
});
console.log("End!");
分析 :
- 主任务中的代码至上而下执行 , 输出 start!
- 遇到 setTimeout , 将它标记为"宏 1", 添加到宏任务队列中 , Macro=[宏 1]
- 遇到 Promise.then, 将它标记为"微 1", 添加到微任务队列中 , Micro=[微 1]
- 继续执行, 输出 console.log("End!");
- 主任务执行结束 , 去清空微任务队列 , 执行"微 1" , 输出 res!
- 去检查宏任务队列 , 执行"宏 1" , 输出 TimeOut!
案例二:
const one = () => Promise.resolve(" One! ");
async function myFunc() {
console.log(" In Function! ");
const res = await one();
console.log(res);
}
console.log(" Before function! ");
myFunc();
console.log(" After function! ");
分析 :
- 主任务中的代码至上而下执行 , 输出 Before function!
- myFunc()函数被调用 , 运行函数体 , 输出 In Function!
- 函数体继续执行 , 当遇到 await 关键字时 , async 函数体中剩余的代码被暂停, 会运行在微任务中而不是一个常规任务!将它标记为"微 1", 添加到微任务队列中 , Micro=[微 1]
- 继续执行全局上下文 , 输出 After function!
- 主任务执行结束 , 去清空微任务队列 , 输出 One!