Promise
Promise 是 ES6 引入的异步编程解决方案,用于解决传统回调函数嵌套 (回调地狱) 的问题,提供了更优雅、可维护的异步代码编写方式;
核心概念
-
什么是 Promise?Promise 是一个对象,代表一个异步操作的最终完成 (或失败) 及其结果值,它有以下核心特征:
- 状态不可逆:Promise 一旦从 「未完成」 转为 「完成 / 失败」,状态永久固定,无法修改;
- 链式调用:通过 .then()/.catch()/.finally() 实现链式调用,替代回调嵌套;
- 异步执行:Promise 的回调函数会被放入微任务队列,而非同步执行;
-
Promise 的三种状态:
状态 描述 能否转换 pending 初始状态,异步操作未完成 可转为 fulfilled/rejected fulfilled 异步操作成功 不可逆 rejected 异步操作失败 不可逆 -
示例代码:
let p1 = new Promise((resolve, reject) => { setTimeout(_ => { // 一旦状态被改变,再次执行 resolve、reject 就没有用了 resolve('ok'); // pending → fulfilled reject('no'); }, 200); }); setTimeout(()=>{ console.log('s', p1); // Promise { 'ok' } }, 1000);
基础用法
构造函数
-
Promise 构造函数接收一个「执行器函数」 (executor),该函数立即执行,参数为两个回调:
- resolve(value):将 Promise 转为 fulfilled,并传递成功结果 value;
- reject(reason):将 Promise 转为 rejected,并传递失败原因 reason;
-
示例代码:
// 封装异步操作(如 AJAX) function fetchData(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText)); // 成功回调 } else { reject(new Error(`请求失败:${xhr.status}`)); // 失败回调 } }; xhr.onerror = () => reject(new Error("网络错误")); xhr.send(); }); } // 使用 fetchData("https://api.example.com/data") .then((data) => console.log("数据:", data)) .catch((error) => console.error("错误:", error));
原型方法(链式调用核心)
-
.then(onFulfilled, onRejected)- 接收两个可选回调:
- onFulfilled:Promise 成功时执行,参数为 resolve 传递的值;
- onRejected:Promise 失败时执行,参数为 reject 传递的原因;
- 返回值:
- 若回调返回普通值 (非 Promise),新 Promise 状态为 fulfilled,值为该返回值;
- 若回调返回 Promise,新 Promise 状态和值继承该 Promise;
- 若回调抛出错误,新 Promise 状态为 rejected,值为抛出的错误;
Promise.resolve(1) .then((v) => v + 1) // 返回 2 → 新 Promise 状态 fulfilled,值 2 .then((v) => Promise.resolve(v * 2)) // 返回 Promise → 新 Promise 值 4 .then((v) => { throw new Error("出错"); }) // 抛出错误 → 新 Promise rejected .catch((err) => console.log(err.message)); // 捕获错误:出错 - 接收两个可选回调:
-
.catch(onRejected)- 等价于
.then(null, onRejected),专门捕获 Promise 链中的错误; - 错误会「冒泡」到最近的
.catch(),未捕获的错误会触发 unhandledrejection 事件;
Promise.reject("失败") .then((v) => console.log(v)) // 跳过(Promise 已失败) .catch((err) => console.log(err)); // 捕获:失败 // 捕获链式中的错误 fetchData("https://api.example.com/data") .then((data) => { if (!data) { throw new Error("数据为空"); // 手动抛出错误 } return data; }) .catch((err) => console.log(err.message)); // 捕获:数据为空 / 请求失败 - 等价于
-
.finally(onFinally)- ES2018 新增,无论 Promise 成功 / 失败,都会执行该回调;
- 不接收参数 (无法判断状态),返回的 Promise 继承原状态和值 (除非 .finally() 抛出错误);
- 常用于「清理操作」 (如关闭加载动画、释放资源);
console.log("开始请求"); fetchData("https://api.example.com/data") .then((data) => console.log("数据:", data)) .catch((err) => console.log("错误:", err)) .finally(() => console.log("请求结束(无论成败)")); // 必执行
Promise 静态方法(工具方法)
-
Promise.resolve(value)- 快速创建一个 fulfilled 状态的 Promise;
- 若 value 是 Promise,则直接返回该 Promise;若 value 有 .then() 方法 (thenable 对象),则转为 Promise 并执行 .then();
// 普通值 const p1 = Promise.resolve(1); p1.then((v) => console.log(v)); // 1 // Promise 对象 const p2 = Promise.resolve(Promise.reject("失败")); p2.catch((err) => console.log(err)); // 失败 // thenable 对象 const thenable = { then(resolve) { resolve("thenable 转为 Promise"); } }; Promise.resolve(thenable).then((v) => console.log(v)); // thenable 转为 Promise -
Promise.reject(reason)- 快速创建一个 rejected 状态的 Promise;
- 注意:reason 不会被解析 (即使是 Promise,也直接作为失败原因);
const p = Promise.reject(new Error("手动失败")); p.catch((err) => console.log(err.message)); // 手动失败 // 区别于 Promise.resolve Promise.reject(Promise.resolve(1)).catch((err) => console.log(err)); // 直接打印 Promise 对象 -
Promise.all(iterable)- 接收一个可迭代对象 (如数组),包含多个 Promise;
- 状态规则:
- 所有 Promise 都 fulfilled → 新 Promise fulfilled,值为所有 Promise 结果的数组 (顺序与输入一致);
- 任意一个 Promise rejected → 新 Promise 立即 rejected,值为第一个失败的原因;
- 适用于「所有异步操作都完成后再执行」的场景 (如批量请求数据);
const p1 = fetchData("https://api.example.com/user"); const p2 = fetchData("https://api.example.com/posts"); Promise.all([p1, p2]) .then(([user, posts]) => { // 数组解构,顺序与输入一致 console.log("用户:", user); console.log("帖子:", posts); }) .catch((err) => console.log("失败原因:", err)); // 第一个失败的错误 -
Promise.allSettled(iterable)- ES2020 新增,等待所有 Promise 完成 (无论成败);
- 新 Promise 始终 fulfilled,值为一个数组,每个元素包含对应 Promise 的状态和结果:
- 成功:
{ status: "fulfilled", value: 结果 }; - 失败:
{ status: "rejected", reason: 原因 };
- 成功:
- 适用于「需要知道所有异步操作结果(无论成败)」的场景 (如批量上传文件);
const p1 = Promise.resolve("成功1"); const p2 = Promise.reject("失败2"); Promise.allSettled([p1, p2]) .then((results) => { results.forEach((result, index) => { if (result.status === "fulfilled") { console.log(`第${index+1}个成功:`, result.value); } else { console.log(`第${index+1}个失败:`, result.reason); } }); }); // 输出: // 第1个成功:成功1 // 第2个失败:失败2 -
Promise.race(iterable)- 「竞速」模式:第一个完成 (成功 / 失败) 的 Promise 决定新 Promise 的状态和值;
- 适用于「超时控制」「最快响应优先」场景 (如同时请求多个 CDN,取最快的);
// 超时控制:5秒内未完成则触发失败 const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error("请求超时")), 5000); }); Promise.race([fetchData("https://api.example.com/data"), timeoutPromise]) .then((data) => console.log("数据:", data)) .catch((err) => console.log(err.message)); // 要么数据返回,要么超时 -
Promise.any(iterable)- ES2021 新增,与 Promise.race 类似,但只关注「第一个成功」的 Promise:
- 任意一个 Promise fulfilled → 新 Promise fulfilled,值为该成功结果;
- 所有 Promise 都 rejected → 新 Promise rejected,值为 AggregateError (包含所有失败原因);
- 适用于「容错请求」 (如多个备用接口,取第一个成功的);
const p1 = Promise.reject("接口1失败"); const p2 = Promise.resolve("接口2成功"); const p3 = Promise.reject("接口3失败"); Promise.any([p1, p2, p3]) .then((value) => console.log("成功:", value)) // 接口2成功 .catch((err) => { console.log("所有接口失败:", err.errors); // [接口1失败, 接口3失败] }); - ES2021 新增,与 Promise.race 类似,但只关注「第一个成功」的 Promise:
关键特性与注意事项
-
状态不可逆:一旦 Promise 转为 fulfilled/rejected,后续调用 resolve/reject 无效:
const p = new Promise((resolve, reject) => { resolve("成功"); reject("失败"); // 无效(状态已固定) }); p.then((v) => console.log(v)); // 成功 -
微任务执行时机:Promise 的回调 (.then()/.catch()/.finally()) 属于微任务,会在当前宏任务执行完、下一个宏任务执行前执行:
console.log("同步代码1"); Promise.resolve().then(() => console.log("Promise 微任务")); console.log("同步代码2"); // 输出顺序:同步代码1 → 同步代码2 → Promise 微任务 -
错误捕获:
- .catch() 只能捕获其前面 Promise 链中的错误,无法捕获自身回调的错误;
- .then() 只能捕获其前面 Promise 链中的错误,无法捕获自身回调的错误;
- 未捕获的 Promise 错误会触发全局 unhandledrejection 事件:
window.addEventListener("unhandledrejection", (event) => { event.preventDefault(); // 阻止控制台报错 console.log("未捕获的 Promise 错误:", event.reason); }); Promise.reject("未捕获的错误"); // 触发上述事件 -
避免回调地狱:Promise 链式调用替代嵌套回调;
// 回调地狱(传统方式) fetchUser((user) => { fetchPosts(user.id, (posts) => { fetchComments(posts[0].id, (comments) => { console.log(comments); }); }); }); // Promise 链式调用(优雅) fetchUser() .then((user) => fetchPosts(user.id)) .then((posts) => fetchComments(posts[0].id)) .then((comments) => console.log(comments)) .catch((err) => console.log(err));
Generator
Generator (生成器) 是 ES6 引入的一种特殊函数,核心特性是可暂停 / 恢复执行,并能逐步产生 (yield) 值,是实现迭代器、异步流程控制 (如替代回调 / Promise 链) 的重要工具;
核心概念
-
生成器函数的定义:生成器函数通过 function* 声明 (注意 * 位置:function* fn() / function *fn() / function * fn() 均可,推荐 function*),内部用 yield 关键字暂停执行并产出值;
// 基础定义 function* generatorFn() { yield 1; yield 2; return 3; // 结束时返回(迭代时不会被遍历到) } -
生成器对象:调用生成器函数不会立即执行函数体,而是返回一个「生成器对象」 (同时实现 Iterator 和 Iterable 接口),这是控制生成器执行的唯一入口;
const gen = generatorFn(); // 无执行,返回生成器对象 console.log(gen); // Object [Generator] {}
核心方法与执行流程
生成器的执行完全由生成器对象的 next() 方法控制,核心流程是:next() → 执行到 yield → 暂停 → 产出值;
再次 next() → 从暂停处恢复执行;
-
next()方法:- 参数:可选,会作为上一个 yield 表达式的返回值;
- 返回值:一个对象,包含两个属性:
- value:当前 yield 产出的值 (或 return 的值);
- done:布尔值,false 表示未完成,true 表示生成器执行结束;
function* genFn() { console.log('开始执行'); const a = yield 1; // 第一个yield,产出1,暂停 console.log('恢复执行,a =', a); const b = yield 2; // 第二个yield,产出2,暂停 console.log('恢复执行,b =', b); return 3; // 结束 } const gen = genFn(); // 无输出(函数未执行) // 第一次调用 next() console.log(gen.next()); // 输出:开始执行 → { value: 1, done: false } // 第二次调用 next(10):参数10作为第一个yield的返回值(赋值给a) console.log(gen.next(10)); // 输出:恢复执行,a = 10 → { value: 2, done: false } // 第三次调用 next(20):参数20作为第二个yield的返回值(赋值给b) console.log(gen.next(20)); // 输出:恢复执行,b = 20 → { value: 3, done: true } // 第四次调用 next():生成器已结束 console.log(gen.next()); // { value: undefined, done: true } -
return()方法:强制终止生成器,返回 { value: 传入的参数, done: true },后续调用 next() 始终返回 { value: undefined, done: true };const gen = genFn(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.return('强制结束'));// { value: '强制结束', done: true } console.log(gen.next()); // { value: undefined, done: true } -
throw()方法:向生成器内部抛出错误,若未捕获则终止执行;若内部有 try/catch,则捕获错误后可继续执行;function* genFn() { try { yield 1; } catch (e) { console.log('捕获错误:', e); yield '错误后恢复'; } yield 2; } const gen = genFn(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.throw(new Error('测试错误'))); // 输出:捕获错误:Error: 测试错误 → { value: '错误后恢复', done: false } console.log(gen.next()); // { value: 2, done: false }
yield* 委托迭代
-
yield* 用于将迭代权委托给另一个可迭代对象 (生成器、数组、字符串等),会逐个产出委托对象的值;
-
委托给生成器
function* gen1() { yield 1; yield 2; } function* gen2() { yield* gen1(); // 委托gen1 yield 3; } const gen = gen2(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: 3, done: false } -
委托给其他可迭代对象
function* gen() { yield* [1, 2, 3]; // 数组 yield* 'ab'; // 字符串 } const genObj = gen(); console.log([...genObj]); // [1, 2, 3, 'a', 'b']
生成器的迭代特性
-
生成器对象实现了 Iterable 接口 (有 [Symbol.iterator] 方法),因此可用于:
- for…of 循环 (自动遍历,直到 done: true,且忽略 return 的值);
- 扩展运算符 …;
- Array.from() 等;
-
示例:for…of 遍历
function* genFn() { yield 1; yield 2; return 3; // for...of 不会遍历此值 } // for...of 自动调用 next(),直到 done: true for (const val of genFn()) { console.log(val); // 1 → 2 } // 扩展运算符 console.log([...genFn()]); // [1, 2]
生成器的典型应用
-
无限迭代器:生成器可轻松创建无限序列 (因惰性执行,不会立即占用内存):
function* infiniteCounter() { let i = 0; while (true) { yield i++; } } const counter = infiniteCounter(); console.log(counter.next().value); // 0 console.log(counter.next().value); // 1 console.log(counter.next().value); // 2 // 按需取值,无内存溢出 -
惰性求值:按需生成数据,避免提前计算所有值 (如大数据集处理):
// 生成指定范围的数(惰性生成,仅在next()时计算) function* range(start, end, step = 1) { for (let i = start; i <= end; i += step) { yield i; } } const nums = range(1, 1000000); console.log(nums.next().value); // 1(仅计算第一个值,无性能损耗)
async、await
async/await 是 ES2017 (ES8) 引入的异步编程语法糖,基于 Promise 实现,旨在解决 Promise 链式调用 (.then().catch()) 的可读性问题,让异步代码的写法更接近同步代码,是目前 JavaScript 异步编程的主流方案;
核心概念
-
async 函数:
- 声明方式:在函数前加 async 关键字 (支持函数声明、函数表达式、箭头函数、方法等);
- 核心特性: async 函数的返回值必然是一个 Promise 对象 (无论函数内部返回什么值):
- 若函数内部返回非 Promise 值 (如数字、字符串),会被包装为 resolved 状态的 Promise;
- 若函数内部抛出错误,会被包装为 rejected 状态的 Promise;
- 若函数内部返回一个 Promise,则直接返回该 Promise (状态由其决定);
// 1. 返回非Promise值 → 包装为resolved的Promise async function fn1() { return 123; } console.log(fn1()); // Promise { 123 } fn1().then(res => console.log(res)); // 123 // 2. 抛出错误 → 包装为rejected的Promise async function fn2() { throw new Error('测试错误'); } fn2().catch(err => console.log(err)); // Error: 测试错误 // 3. 返回Promise → 直接返回该Promise async function fn3() { return Promise.resolve('hello'); } fn3().then(res => console.log(res)); // hello -
await 关键字:
- 作用:暂停 async 函数的执行,等待右侧 Promise 完成 (resolved/rejected) 后恢复执行,并返回 Promise 的解析值;若右侧不是 Promise,则直接返回该值;
- 使用限制:await 只能在 async 函数内部使用 (普通函数中使用会抛出语法错误);
// 模拟异步请求 function fetchData() { return new Promise(resolve => { setTimeout(() => resolve('异步数据'), 1000); }); } async function getData() { console.log('开始请求'); const data = await fetchData(); // 暂停执行,等待Promise resolved console.log('请求完成:', data); // 1秒后输出:请求完成:异步数据 return data; } getData();
执行流程
-
async/await 的执行流程核心是 「暂停 - 恢复」,本质是 Generator + Promise 的语法糖 (浏览器 / Node 底层自动实现了 Generator 的执行逻辑);
-
分步解析执行流程:
// 模拟异步操作 const delay = (time) => new Promise(resolve => { setTimeout(() => resolve(`延迟${time}ms`), time); }); async function asyncFn() { console.log('步骤1:执行同步代码'); const res1 = await delay(1000); // 暂停,等待Promise完成 console.log('步骤2:', res1); // 1秒后恢复执行 const res2 = await delay(500); // 再次暂停 console.log('步骤3:', res2); // 再500ms后恢复执行 return '最终结果'; } // 调用async函数 asyncFn().then(res => console.log('步骤4:', res)); /* 输出顺序: 步骤1:执行同步代码 → (等待1秒) 步骤2:延迟1000ms → (等待500ms) 步骤3:延迟500ms → 步骤4:最终结果 */ -
关键流程总结:
- 调用 async 函数时,函数立即开始执行同步代码,直到遇到第一个 await;
- 遇到 await 后,暂停 async 函数执行,将右侧的 Promise 放入微任务队列;
- 主线程继续执行其他代码,直到微任务队列执行;
- 当 await 右侧的 Promise 状态变为 resolved,async 函数恢复执行,await 返回 Promise 的解析值;若 Promise 变为 rejected,则抛出错误 (需捕获);
- 继续执行后续代码,直到下一个 await 或函数结束,最终返回一个 Promise;
错误处理
-
try/catch 捕获 (推荐):在 async 函数内部用 try/catch 包裹 await 语句,捕获单个 / 多个异步操作的错误:
async function handleError() { try { // 模拟rejected的Promise const res = await Promise.reject('请求失败'); console.log(res); // 不会执行 } catch (err) { console.log('捕获错误:', err); // 捕获错误:请求失败 } // 多个await的错误捕获(可分开/统一捕获) try { await delay(1000); await Promise.reject('第二个错误'); } catch (err) { console.log('捕获多个错误:', err); // 捕获多个错误:第二个错误 } } handleError(); -
链式 catch 捕获:利用 async 函数返回 Promise 的特性,在函数调用后用 .catch() 捕获所有未处理的错误:
async function fn() { await Promise.reject('全局错误'); } fn().catch(err => console.log('全局捕获:', err)); // 全局捕获:全局错误 -
混合捕获 (精准 + 兜底):
async function mixCatch() { try { await Promise.reject('精准错误'); } catch (err) { console.log('精准捕获:', err); // 处理已知错误 throw new Error('重新抛出的错误'); // 可重新抛出,由外层catch处理 } } mixCatch().catch(err => console.log('兜底捕获:', err)); // 兜底捕获:Error: 重新抛出的错误
await 的并行 / 串行执行
-
串行执行 (默认):多个 await 依次执行,前一个完成后才执行后一个,总耗时为各异步操作耗时之和:
async function serial() { const res1 = await delay(1000); // 1秒 const res2 = await delay(500); // 再500ms // 总耗时:1500ms console.log(res1, res2); } -
并行执行 (优化性能):若多个异步操作无依赖,可通过 Promise.all 实现并行执行,总耗时为最长的异步操作耗时:
async function parallel() { // 先创建所有Promise(并行开始) const promise1 = delay(1000); const promise2 = delay(500); // 再await所有结果 const res1 = await promise1; const res2 = await promise2; // 总耗时:1000ms(最长的那个) console.log(res1, res2); } // 更简洁的写法 async function parallelAll() { const [res1, res2] = await Promise.all([delay(1000), delay(500)]); console.log(res1, res2); } -
部分并行 + 部分串行:
async function mix() { // 第一步:并行执行两个异步操作 const [res1, res2] = await Promise.all([delay(1000), delay(500)]); // 第二步:基于第一步的结果,串行执行下一个异步操作 const res3 = await delay(800, res1 + res2); console.log(res3); // 总耗时:1000 + 800 = 1800ms }
async/await 与 Generator 的关系
-
async/await 是 Generator + Promise 的语法糖,以下是手动用 Generator 模拟 async/await 的核心逻辑,帮助理解底层实现;
-
模拟实现 (简化版 co 库):
// 手动执行生成器(模拟 co 库的核心逻辑) function run(genFn) { const gen = genFn(); // 获取生成器对象 // 递归执行next function next(res) { const { value, done } = gen.next(res); if (done) return Promise.resolve(value); // 生成器结束,返回Promise // 若value是Promise,解析后继续执行;否则直接传入下一个next return Promise.resolve(value).then(next); } return next(); } // 模拟异步操作 const delay = (time) => new Promise(resolve => { setTimeout(() => resolve(`延迟${time}ms`), time); }); // 用Generator模拟async函数 function* genFn() { const res1 = yield delay(1000); const res2 = yield delay(500); return res1 + res2; } // 执行(效果等同于async/await) run(genFn).then(res => console.log(res)); // 延迟1000ms延迟500ms -
核心差异:
特性 Generator + Promise async/await 执行方式 需手动编写执行器 (如 co) 语言层面自动执行,无需手动 错误处理 需手动捕获 throw () 原生支持 try/catch 语法简洁性 繁琐 (需 yield/next) 简洁 (接近同步代码) 语义化 弱 (迭代器语义) 强 (异步语义)
面试题
中断 Promise 的 .then 链
-
核心原理:.then 回调的执行依赖前一个 Promise 的状态:
- 若前一个 Promise 是 pending 状态,后续 .then 会等待其状态变更,永远不会执行;
- 若前一个 Promise 是 rejected 但后续无 .catch,错误会冒泡到全局,但 .then 仍会跳过;
- 若返回 rejected 且后续 .catch 捕获后不返回新值,链会继续 (需避免);
-
实现方式:
// 工具函数:创建永久 pending 的 Promise const pausePromise = () => new Promise(() => {}); // 中断 then 链示例 Promise.resolve(1) .then((v) => { console.log("第一步:", v); // 执行 if (v === 1) { return pausePromise(); // 返回 pending Promise,中断后续 } return v + 1; }) .then((v) => { console.log("第二步:", v); // 永不执行 }) .catch((err) => { console.log("捕获错误:", err); // 也不执行(无错误) });Promise.resolve(1) .then((v) => { console.log("第一步:", v); if (v === 1) { return Promise.reject("中断链"); // 返回 rejected } return v + 1; }) .then((v) => { console.log("第二步:", v); // 跳过(前一个 Promise 失败) }); // 注意:未捕获的 rejected 会触发 `unhandledrejection` 事件,需谨慎Promise.resolve(1) .then((v) => { throw new Error("触发中断"); }) .catch((err) => { console.log("捕获错误:", err.message); // 执行 return pausePromise(); // 中断后续 then }) .then((v) => { console.log("后续步骤:", v); // 永不执行 });
中断 Promise 的本质(Promise 状态不可逆)
-
Promise 的核心特性是状态不可逆:一旦转为 fulfilled/rejected,无法回退到 pending,也无法修改结果;因此,「中断 Promise」并非「取消已执行的异步操作」,而是:
- 阻止 Promise 状态变更 (如不调用 resolve/reject);
- 阻止 Promise 结果的后续处理 (即中断 .then 链);
- (进阶)取消异步操作本身 (如 AbortController 中断 axios 请求);
-
实现方式:
const unresolvablePromise = new Promise((resolve, reject) => { // 不调用 resolve/reject,Promise 永久 pending }); unresolvablePromise .then(() => console.log("成功")) // 不执行 .catch(() => console.log("失败")); // 不执行const controller = new AbortController(); const signal = controller.signal; // 封装带中断的请求 const fetchWithAbort = (url) => { return new Promise((resolve, reject) => { fetch(url, { signal }) .then((res) => res.json()) .then(resolve) .catch((err) => { if (err.name === "AbortError") { console.log("请求已中断"); return; // 不 reject,让 Promise 保持 pending } reject(err); }); }); }; // 使用 const requestPromise = fetchWithAbort("https://api.example.com/data"); requestPromise.then((data) => console.log("数据:", data)); // 500ms 后中断请求 setTimeout(() => { controller.abort(); // 真实取消请求,触发 AbortError }, 500);/** * 封装可中断的 Axios 请求 * @param {string} url - 请求地址 * @param {AxiosRequestConfig} options - Axios 配置 * @returns {Object} { promise: Promise, abort: Function } */ function createAbortableAxios(url, options = {}) { const controller = new AbortController(); const signal = controller.signal; // 封装 Axios Promise const axiosPromise = new Promise((resolve, reject) => { axios({ url, ...options, signal, // 注入 signal }) .then((response) => { resolve(response.data); // 直接返回响应数据 }) .catch((err) => { // 区分 Axios 中断错误 if (axios.isCancel(err)) { reject(new Error("Axios 请求被手动取消")); } else if (err.name === "AbortError") { reject(new Error("Axios 请求被 AbortController 中断")); } else { reject(err); // 其他错误(网络/超时/业务错误) } }); }); return { promise: axiosPromise, abort: () => { controller.abort(); // 中断请求 // 兼容旧版 CancelToken(可选) // const source = axios.CancelToken.source(); // source.cancel("请求取消"); }, }; } // 测试 const { promise: axiosPromise, abort: abortAxios } = createAbortableAxios("https://api.example.com/user", { method: "POST", data: { id: 1 } }); axiosPromise .then((data) => console.log("Axios 请求成功:", data)) .catch((err) => console.log("Axios 错误:", err.message)); // 点击按钮中断请求(真实业务场景) document.querySelector("#cancelBtn").addEventListener("click", () => { abortAxios(); });
defer 的作用(延迟解析 Promise)
-
defer 并非 ES6+ 原生 API,而是一种设计模式 (Deferred 模式),核心是将 Promise 的 resolve/reject 暴露到外部,实现 「延迟控制 Promise 状态」;
-
原生 Promise 的痛点:原生 Promise 的 resolve/reject 只能在执行器内部调用,若需在外部触发 Promise 状态变更 (如事件回调、定时器外),则需 Deferred 模式;
class Deferred { constructor() { // 创建 Promise,并将 resolve/reject 挂载到实例上 this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } } -
实现方式:
// 等待按钮点击 function waitButtonClick(btn) { const deferred = new Deferred(); btn.addEventListener("click", (e) => { deferred.resolve(e); // 外部触发 resolve }, { once: true }); return deferred.promise; } // 使用 const btn = document.querySelector("#btn"); waitButtonClick(btn).then((e) => { console.log("按钮点击:", e); });const deferred = new Deferred(); // 多个依赖该 Promise 的回调 deferred.promise.then(() => console.log("操作1完成")); deferred.promise.then(() => console.log("操作2完成")); // 手动控制时机(如用户点击确认后) document.querySelector("#confirm").addEventListener("click", () => { deferred.resolve(); // 一次性触发所有回调 });function connectWebSocket(url) { const deferred = new Deferred(); const ws = new WebSocket(url); ws.onopen = () => deferred.resolve(ws); // 连接成功解析 ws.onerror = (err) => deferred.reject(err); // 失败拒绝 return deferred.promise; } // 使用 connectWebSocket("ws://example.com") .then((ws) => { ws.send("Hello"); }) .catch((err) => console.log("连接失败:", err));
封装一个延时微任务
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function test() {
var temple = await sleep(1000);
console.log(1111);
return temple;
}
test();
function* sleep(ms) {
yield new Promise((resolve, reject) => setTimeout(resolve, ms));
}
sleep(500).next().value.then(function () {
console.log(2222)
});
看程序写结果(头条、字节)
-
题目
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); // 浏览器环境:识别 await 后面跟的是 promise 的话默认就会直接调用 promise.then // 等价于 async2().then(() => console.log('async1 end')) // node 低版本环境:node 识别不出 await,则直接用 promsie 的 resolve 包裹起来,resole 里面是 promise 会调用 then,然后再调用 then(() => console.log('async1 end')),相当于调了两次 then,所以结果和浏览器的不太一致 // new Promise((resolve, reject) => resolve(async2())).then(() => console.log('async1 end')); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function () { console.log('setTimeout'); }, 0) async1(); new Promise(function (resolve) { console.log('promise1'); resolve(); }).then(function () { console.log('promise2'); }); console.log('script end'); // 浏览器环境执行: // script start // async1 start // async2 // promise1 // script end // async1 end // promise2 // setTimeout -
图解
看程序写结果(字节)
-
题目
console.log(1); setTimeout(_ => { console.log(2); }, 1000); async function fn() { console.log(3); setTimeout(_ => { console.log(4); }, 20); return Promise.reject(); } async function run() { console.log(5); await fn(); console.log(6); } run(); // 需要执行 150MS 左右 for (let i = 0; i < 90000000; i++) { } setTimeout(_ => { console.log(7); new Promise(resolve => { console.log(8); resolve(); }).then(_ => { console.log(9); }); }, 0); console.log(10); // 1 // 5 // 3 // 10 // 4 // 7 // 8 // 9 // 2 -
图解
看程序写结果
async function m1() {
return 1;
}
async function m2() {
const n = await m1();
console.log(n);
return 2;
}
async function m3() {
const n = m2(); // 没有 await,返回值是 Promise<pending>
console.log(n);
return 3;
}
m3().then((n) => {
console.log(n);
});
m3();
console.log(4);
// Promise { <pending> }
// Promise { <pending> }
// 4
// 1
// 3
// 1
var a;
var b = new Promise((resolve, reject) => {
console.log('promise1');
setTimeout(() => {
resolve();
}, 1000);
})
.then(() => {
console.log('promise2');
})
.then(() => {
console.log('promise3');
})
.then(() => {
console.log('promise4');
});
a = new Promise(async (resolve, reject) => {
console.log(a);
await b;
console.log(a);
console.log('after1');
await a;
resolve(true);
console.log('after2');
});
console.log('end');
// promise1
// undefined
// end
// promise2
// promise3
// promise4
// Promise { <pending> }
// after1
Promise.resolve(1).then((res) => {
console.log(res)
return 2
}).catch((err) => {
return 3
}).then((res) => {
console.log(res)
})
// 1
// 2
// 第一个 then 中没有异常,catch 捕获不到异常,则执行第二个 then
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
const promise2 = promise1.then(() => {
throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
console.log('promise1~', promise1)
console.log('promise2~', promise2)
}, 2000)
// promise1 Promise {<pending>}
// promise2 Promise {<pending>}
// promise1~ Promise {<fulfilled>: 'success'}
// promise2~ Promise {<rejected>: Error: error!!!}
// promise1、promise2 都是微任务执行,此时等待返回结果,状态为 pending
// 2s 后再次输出 promise1、promise2,此时微任务执行完毕,状态更新
setTimeout(() => console.log(5), 0);
new Promise(resolve => {
console.log(1);
resolve(3);
Promise.resolve().then(() => console.log(4))
}).then(num => {
console.log(num)
});
console.log(2);
// 1
// 2
// 4
// 3
// 5
// promise 构造函数里面是同步代码,构造函数的 resolve/reject 是异步的
// Promise.resolve().then 也是异步,此行代码先于 resolve 回调
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
// then: Error: error!!!
// return 一个 error 对象并不会抛出错误,所以不会被后续的 catch 捕获
// 改成下面这样会被捕获异常
// return Promise.reject(new Error(‘error!!!’))
// throw new Error(‘error!!!’)
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1
// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
// 链式调用的参数不是函数,会发生值透传,传入的非函数值会被忽略
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
// 3
// 7
// 4
// 1
// 2
// 5
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
// The Fails!
// The Fails!
var p = new Promise((resolve, reject) => {
// 创建并返回一个被拒绝(rejected)的 Promise 对象,在这个 Promise 对象被拒绝时,它将抛出一个包含错误信息的 Error 对象
return Promise.reject(new Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
// Error: The Fails!
// at /Users/wushuai/Desktop/javascript/1.js:2:27
// at new Promise (<anonymous>)
// at Object.<anonymous> (/Users/wushuai/Desktop/javascript/1.js:1:9)
// at Module._compile (node:internal/modules/cjs/loader:1254:14)
// at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
// at Module.load (node:internal/modules/cjs/loader:1117:32)
// at Module._load (node:internal/modules/cjs/loader:958:12)
// at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
// at node:internal/main/run_main_module:23:47
async function async1() {
console.log(1);
const result = await async2();
console.log(3);
}
async function async2() {
console.log(2);
}
Promise.resolve().then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
});
async1();
console.log(6);
// 1
// 2
// 6
// 4
// 3
// 5
Promise.resolve('Success!').then(() => {
throw Error('Oh noes!')
}).catch(error => {
console.log('actually, that worked');
return 'actually, that worked'
}).then(data => {
throw Error('The fails!')
}).catch(error => console.log(error.message))
// actually, that worked
// The fails!
new Promise((resolve, reject) => {
resolve('Success!')
}).then(() => {
throw Error('Oh noes!')
}).catch(error => {
console.log('catch=>' + error)
return "actually, that worked"
}).catch(error => console.log(error.message))
// catch=>Error: Oh noes!
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
.catch(error => console.log('catch=>' + error))
.then(error => console.log('then=>' + error))
// catch=> Error: The Fails!
// then => undefined
第 4️⃣ 座大山:Ajax
上一篇