当前位置: 首页 > news >正文

深入解析Promise:从基础原理到async/await实战

Promise 是 ES6 引入的异步编程解决方案,本质是一个表示异步操作最终完成或失败的对象。它有三种状态:​

  • pending(进行中):初始状态,尚未完成或失败​
  • fulfilled(已完成):异步操作成功完成​
  • rejected(已拒绝):异步操作失败

一、Promise 构造函数

  • 语法new Promise((resolve, reject) => { /* 异步操作 */ })
  • 参数
    • resolve:是一个函数,当异步操作成功完成时调用,它接收一个参数,该参数会成为 Promise 解决(fulfilled)状态下.then()回调函数的参数。
    • reject:也是一个函数,当异步操作失败时调用,它接收一个参数,通常是一个错误对象,该对象会成为 Promise 拒绝(rejected)状态下.catch()回调函数的参数。
// 修改为函数形式,每次调用返回一个新的 Promise
const getRandomNumber = () => {return new Promise((resolve, reject) => {const random = Math.random();if (random > 0.5) {resolve(random);} else {reject(new Error('随机数小于0.5'));}});
};// 第一次调用
getRandomNumber().then(result => console.log('第一次结果:', result)).catch(error => console.log('第一次失败:', error));// 第二次调用
getRandomNumber().then(result => console.log('第二次结果:', result)).catch(error => console.log('第二次失败:', error));
  • 函数形式:将 getRandomNumber 定义为函数,每次调用 getRandomNumber() 会创建一个新的 Promise,确保两次调用独立执行。
  • 处理成功和失败:每个调用都通过 .then() 处理成功结果,通过 .catch() 处理错误,确保无论随机数是否大于 0.5,都能看到输出。

二、Promise 中 resolve 和 reject 的源码解析与工作原理

在 JavaScript 引擎中,Promise构造函数里的resolvereject是其内部实现的核心部分,它们主要用于控制Promise对象状态的转变。虽然 JavaScript 引擎的底层实现是用 C++ 等语言编写,无法直接查看源码,但可以通过模拟实现来理解其工作原理和参数传入后的执行逻辑。

Promise对象通常包含状态属性、用于存储成功结果或失败原因的属性,以及存放then方法中回调函数的队列。以下是一个简化的Promise模拟实现:

function MyPromise(executor) {// 初始状态为pendingthis.status = 'pending'; // 成功的值,初始为nullthis.value = null; // 失败的原因,初始为nullthis.reason = null; // 成功回调函数队列this.onFulfilledCallbacks = []; // 失败回调函数队列this.onRejectedCallbacks = []; // 定义resolve函数const resolve = (val) => {if (this.status === 'pending') {this.status = 'fulfilled';this.value = val;// 依次执行成功回调函数队列中的函数this.onFulfilledCallbacks.forEach(callback => callback(this.value)); }};// 定义reject函数const reject = (err) => {if (this.status === 'pending') {this.status ='rejected';this.reason = err;// 依次执行失败回调函数队列中的函数this.onRejectedCallbacks.forEach(callback => callback(this.reason)); }};try {// 执行传入的executor函数,并传入resolve和rejectexecutor(resolve, reject); } catch (error) {// 如果executor执行过程中抛出错误,调用rejectreject(error); }
}// 模拟then方法
MyPromise.prototype.then = function (onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function'? onFulfilled : val => val;onRejected = typeof onRejected === 'function'? onRejected : err => { throw err };if (this.status === 'fulfilled') {return new MyPromise((resolve, reject) => {try {const x = onFulfilled(this.value);resolvePromise(this, x, resolve, reject);} catch (error) {reject(error);}});}if (this.status ==='rejected') {return new MyPromise((resolve, reject) => {try {const x = onRejected(this.reason);resolvePromise(this, x, resolve, reject);} catch (error) {reject(error);}});}if (this.status === 'pending') {return new MyPromise((resolve, reject) => {this.onFulfilledCallbacks.push(() => {try {const x = onFulfilled(this.value);resolvePromise(this, x, resolve, reject);} catch (error) {reject(error);}});this.onRejectedCallbacks.push(() => {try {const x = onRejected(this.reason);resolvePromise(this, x, resolve, reject);} catch (error) {reject(error);}});});}
};function resolvePromise(promise2, x, resolve, reject) {if (promise2 === x) {return reject(new TypeError('Chaining cycle detected for promise'));}if (x instanceof MyPromise) {x.then(resolve, reject);} else if (x!== null && (typeof x === 'object' || typeof x === 'function')) {let called = false;try {const then = x.then;if (typeof then === 'function') {then.call(x, y => {if (called) return;called = true;resolvePromise(promise2, y, resolve, reject);}, err => {if (called) return;called = true;reject(err);});} else {resolve(x);}} catch (error) {if (called) return;called = true;reject(error);}} else {resolve(x);}
}

1.resolve 函数的工作原理

resolve函数的主要作用是将Promisepending状态转换为fulfilled状态,并处理传入的参数。当resolve被调用时,参数传入后具体执行过程如下:

  • 检查状态:首先判断当前Promise的状态是否为pending,只有在pending状态下才进行状态转换。
  • 更新状态和值:将Promise的状态设置为fulfilled,并将传入的参数val赋值给this.value,用于后续.then()回调函数获取异步操作的结果。
  • 执行回调队列:遍历onFulfilledCallbacks数组,依次执行其中的回调函数,并将this.value作为参数传入。如果在Promise处于pending状态时就调用了.then()方法,对应的成功回调函数会被存入onFulfilledCallbacks队列,在resolve执行时触发执行。

例如:

const promise = new MyPromise((resolve, reject) => {setTimeout(() => {const data = { message: "操作成功" };resolve(data);}, 1000);
});promise.then(result => {console.log(result);
});

上述代码中,1 秒后resolve被调用,传入对象{ message: "操作成功" }Promise状态变为fulfilledthis.value被赋值为该对象,随后.then()中的回调函数执行,输出该对象。

2.reject 函数的工作原理

reject函数的作用是将Promisepending状态转换为rejected状态,并处理传入的错误参数。当reject被调用时,参数传入后的执行流程如下:

  • 检查状态:同样先判断Promise的状态是否为pending,只有pending状态下才会进行后续操作。
  • 更新状态和原因:将Promise的状态设置为rejected,把传入的错误对象或原因err赋值给this.reason,用于后续.catch()回调函数捕获错误信息。
  • 执行回调队列:遍历onRejectedCallbacks数组,依次执行其中的回调函数,并将this.reason作为参数传入。若在Promisepending时调用了.then()方法且提供了失败回调,或调用了.catch()方法,对应的回调函数会被存入onRejectedCallbacks队列,在reject执行时触发。

示例:

const promise = new MyPromise((resolve, reject) => {setTimeout(() => {const error = new Error("操作失败");reject(error);}, 1500);
});promise.catch(error => {console.error(error.message);
});

这里 1.5 秒后reject被调用,传入Error对象,Promise进入rejected状态,this.reason被赋值为该错误对象,.catch()中的回调函数捕获并输出错误信息。

3.特殊情况处理

  • resolve 传入 Promise 对象:当resolve传入的参数是另一个Promise对象时,当前Promise的状态会跟随传入的Promise状态变化。在模拟实现的resolvePromise函数中,会对这种情况进行处理,等待传入的Promise状态确定后,再根据其结果决定当前Promisefulfilled还是rejected
  • resolve 传入带有 then 方法的对象:如果resolve传入的参数是一个对象或函数,且包含then方法,会将其视为一个类Promise对象,调用其then方法,并根据then方法的执行结果来决定当前Promise的状态 。

三、resolve 和 reject的使用

1. resolve 函数详解

resolve函数用于将 Promise 从pending状态转换为fulfilled状态,意味着异步操作成功完成。当resolve被调用时,它接收的参数会作为 Promise 解决后的值,传递给后续.then()回调函数。

⑴.基本使用示例

// 使用resolve将Promise置为fulfilled状态
const getSuccessMessage = new Promise((resolve, reject) => {setTimeout(() => {const message = "异步操作成功";resolve(message);}, 1000);
});getSuccessMessage.then(result => {console.log(result);
});

在上述代码中,通过setTimeout模拟一个异步操作,1 秒后调用resolve函数,将字符串"异步操作成功"作为参数传入。此时,getSuccessMessage这个 Promise 进入fulfilled状态,.then()回调函数接收到该字符串并进行输出。

⑵.传入不同类型参数

resolve的参数可以是任意合法的 JavaScript 值,包括普通值、对象、数组,甚至是另一个 Promise 对象。

  • 传入普通值
const numberPromise = new Promise((resolve, reject) => {resolve(42);
});
numberPromise.then(value => {console.log('接收到的数值:', value);
});

传入对象

const userPromise = new Promise((resolve, reject) => {const user = { name: "Alice", age: 30 };resolve(user);
});
userPromise.then(userInfo => {console.log('用户信息:', userInfo);
});

  • 传入 Promise 对象
const innerPromise = new Promise((innerResolve) => {setTimeout(() => {innerResolve("内部Promise完成");}, 500);
});const outerPromise = new Promise((resolve, reject) => {resolve(innerPromise);
});outerPromise.then(result => {console.log(result);
});

resolve传入另一个 Promise 对象时,外部 Promise 的状态会跟随内部 Promise 的状态变化,直到内部 Promise 状态确定,外部 Promise 才会完成状态转换。

2. reject 函数详解

reject函数用于将 Promise 从pending状态转换为rejected状态,表示异步操作失败。它接收的参数通常是一个错误对象,会传递给后续.catch()回调函数用于错误处理。

⑴.基本使用示例

// 使用reject将Promise置为rejected状态
const getErrorMessage = new Promise((resolve, reject) => {setTimeout(() => {const error = new Error("异步操作失败");reject(error);}, 1500);
});getErrorMessage.catch(error => {console.error(error.message);
});

这里同样使用setTimeout模拟异步操作,1.5 秒后调用reject函数,传入一个Error对象。getErrorMessage这个 Promise 进入rejected状态,.catch()回调函数捕获到错误信息并进行输出。

⑵.自定义错误类型

除了使用内置的Error对象,也可以自定义错误类型,以便更清晰地标识不同类型的错误。

class CustomError extends Error {constructor(message) {super(message);this.name = "CustomError";}
}const customPromise = new Promise((resolve, reject) => {setTimeout(() => {const customError = new CustomError("自定义错误发生");reject(customError);}, 800);
});customPromise.catch(error => {if (error instanceof CustomError) {console.error('自定义错误:', error.message);} else {console.error('其他错误:', error.message);}
});

通过自定义错误类型,可以在捕获错误时进行更细致的处理,增强代码的健壮性和可维护性。

3. resolve 和 reject 的组合使用

在实际场景中,异步操作可能存在成功和失败两种情况,此时就需要合理使用resolvereject。以下是一个模拟网络请求的示例:

function mockFetch(url) {return new Promise((resolve, reject) => {const isSuccess = Math.random() > 0.3; // 模拟70%的成功率setTimeout(() => {if (isSuccess) {const data = { message: `请求${url}成功` };resolve(data);} else {const error = new Error(`请求${url}失败`);reject(error);}}, 1000);});
}mockFetch('https://example.com/api').then(result => console.log(result)).catch(error => console.error(error.message));

mockFetch函数中,通过随机数模拟网络请求的成功或失败,根据结果调用resolvereject,后续通过.then().catch()分别处理成功和失败的情况。

四、Promise的方法

1. then () 方法

  • 语法promise.then(onFulfilled, onRejected)
  • 参数
    • onFulfilled:当 Promise 进入 fulfilled 状态时执行的回调函数,该函数接收 Promise 解决的值作为参数。该参数可选,如果不提供,Promise 解决的值会沿着链式调用向后传递 。
    • onRejected:当 Promise 进入 rejected 状态时执行的回调函数,该函数接收 Promise 被拒绝的原因作为参数。该参数可选,如果不提供,rejected 状态会传递到下一个.catch()中处理。
  • 示例
getRandomNumber.then(value => console.log('成功获取随机数:', value)).catch(error => console.error('获取随机数失败:', error.message));

2. catch () 方法

  • 语法promise.catch(onRejected)
  • 参数onRejected是一个回调函数,与.then()方法中的onRejected作用相同,用于处理 Promise 被拒绝的情况,接收拒绝原因作为参数。
  • 示例
getRandomNumber.then(value => {// 模拟后续可能失败的操作if (value < 0.8) {throw new Error('随机数不满足条件');}return value;}).catch(error => console.error('操作失败:', error.message));

3.finally () 方法

  • 语法promise.finally(onFinally)
  • 参数onFinally是一个回调函数,无论 Promise 最终是 fulfilled 还是 rejected 状态,该函数都会执行,且不接收任何参数。
  • 示例
getRandomNumber.then(value => console.log('成功获取随机数:', value)).catch(error => console.error('获取随机数失败:', error.message)).finally(() => console.log('Promise操作已结束'));

4.Promise.all () 方法

  • 语法Promise.all(iterable)
  • 参数iterable是一个可迭代对象,比如数组,数组中的每个元素都应该是一个 Promise 对象。
  • 返回值与执行逻辑
    • iterable中的所有 Promise 都变为 fulfilled 状态时,Promise.all()返回的 Promise 才会进入 fulfilled 状态,其解决值是一个数组,数组元素按iterable中 Promise 的顺序对应每个 Promise 的解决值。
    • 只要iterable中有一个 Promise 进入 rejected 状态,Promise.all()返回的 Promise 就会立即进入 rejected 状态,其拒绝原因是第一个进入 rejected 状态的 Promise 的拒绝原因。
  • 示例
const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1500));
const promise3 = new Promise((_, reject) => setTimeout(() => reject(new Error('Promise3失败')), 500));Promise.all([promise1, promise2, promise3]).then(values => console.log('所有Promise成功:', values)).catch(error => console.error('有Promise失败:', error.message));

5. Promise.race () 方法

  • 语法Promise.race(iterable)
  • 参数:同样接收一个可迭代对象,元素为 Promise 对象。
  • 返回值与执行逻辑Promise.race()返回的 Promise 会在iterable中任意一个 Promise 率先改变状态(无论是 fulfilled 还是 rejected)时,随之改变状态。其状态和值与率先改变状态的 Promise 一致。
  • 示例
const promiseA = new Promise((resolve) => setTimeout(() => resolve('A完成'), 2000));
const promiseB = new Promise((resolve) => setTimeout(() => resolve('B完成'), 1000));Promise.race([promiseA, promiseB]).then(result => console.log('率先完成的结果:', result));

6. Promise.resolve () 方法

  • 语法Promise.resolve(value)
  • 参数value可以是任意合法的 JavaScript 值,包括 Promise 对象、普通值。
  • 返回值与执行逻辑
    • 如果value是一个 Promise 对象,直接返回该 Promise 对象。
    • 如果value是一个普通值,返回一个新的已解决状态(fulfilled)的 Promise 对象,其解决值为value
  • 示例
const resolvedPromise = Promise.resolve(42);
const existingPromise = new Promise((resolve) => resolve('已存在的Promise'));resolvedPromise.then(value => console.log('普通值转换的Promise:', value));
Promise.resolve(existingPromise).then(value => console.log('Promise转换的Promise:', value));

7.Promise.reject () 方法

  • 语法Promise.reject(reason)
  • 参数reason通常是一个错误对象,用于表示 Promise 被拒绝的原因。
  • 返回值与执行逻辑:返回一个已拒绝状态(rejected)的 Promise 对象,其拒绝原因就是传入的reason参数。
  • 示例
const rejectedPromise = Promise.reject(new Error('手动拒绝的Promise'));
rejectedPromise.catch(error => console.error('Promise被拒绝:', error.message));

五、async await

async 和 await 是 JavaScript 里用于处理异步操作的语法糖,它们构建于 Promise 之上,能让异步代码以同步的形式书写,增强代码的可读性与可维护性。下面为你详细介绍:

1. async 函数

借助 async 关键字可定义异步函数,该函数会自动返回一个 Promise 对象。当函数执行完毕时,返回值会被包装进 Promise 的 resolve 中;若函数抛出异常,异常会被包装进 Promise 的 reject 中。

async function exampleAsyncFunction() {return 'Hello, World!';
}exampleAsyncFunction().then(result => {console.log(result); // 输出: Hello, World!
});

2.await 表达式

await 仅能在 async 函数内部使用。它会暂停 async 函数的执行,直至所等待的 Promise 被解决(resolved)或者被拒绝(rejected),之后才会恢复 async 函数的执行,并返回 Promise 的解决值。

function delay(ms) {return new Promise(resolve => setTimeout(resolve, ms));
}async function exampleWithAwait() {console.log('Before await');await delay(2000); // 等待 2 秒console.log('After await');
}exampleWithAwait();

3. 处理错误

在 async 函数里,可以使用 try...catch 语句来处理 await 表达式可能出现的错误。

function fetchData() {return new Promise((resolve, reject) => {setTimeout(() => {reject(new Error('Data fetch failed'));}, 1000);});
}async function handleError() {try {await fetchData();} catch (error) {console.error('Error:', error.message);}
}handleError();

4. 并行处理

尽管 await 会暂停函数的执行,但你能够通过 Promise.all 来并行处理多个异步操作。

function asyncTask1() {return new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 1000));
}function asyncTask2() {return new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 1500));
}async function parallelTasks() {const [result1, result2] = await Promise.all([asyncTask1(), asyncTask2()]);console.log(result1);console.log(result2);
}parallelTasks();

通过 async 和 await,异步代码的编写和理解变得更加容易,就像同步代码一样直观。

http://www.xdnf.cn/news/181549.html

相关文章:

  • 4月27日星期日今日早报简报微语报早读
  • 牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记3-地图基本操作与实战案例
  • 二叉树遍历(C语言版)
  • 解决升级WIN11(WINSERVER2025)后 远程桌面内 部分内容 显示 花屏 替换文件
  • 【Luogu】动态规划六
  • Python中数据切片操作详解和代码示例
  • AI实战SEO关键词优化法
  • 【视频生成模型】通义万相Wan2.1模型本地部署和LoRA微调
  • 初中级前端面试全攻略:自我介绍模板、项目讲解套路与常见问答
  • LeetCode42_接雨水
  • 杭电oj(1010、1015、1241)题解
  • 【数据可视化-39】2009-2019年亚马逊50大畅销书数据集可视化分析
  • 迷你世界UGC3.0脚本Wiki世界模块管理接口 World
  • Mysql中隐式内连接和显式内连接的区别
  • (26)VTK C++开发示例 ---将点坐标写入PLY文件
  • linux:进程的替换
  • 大模型时代具身智能:从理论突破到产业落地的全链路解析
  • 自动伴随无人机说明文档
  • Netmiko 源码关键流程图
  • pytorch学习使用
  • 深入解析MyBatis-Plus中的lambdaUpdate与lambdaQuery
  • OpenCV 图形API(65)图像结构分析和形状描述符------拟合二维点集的直线函数 fitLine2D()
  • 文章记单词 | 第47篇(六级)
  • java map中的key区分大小写吗
  • ChatGPT与DeepSeek在科研论文撰写中的整体科研流程与案例解析
  • 【git】添加项目到已有gitee仓库
  • vue组件间通信
  • 蓝桥杯 9.生命之树
  • 【Multipath】dm软链接相关问题定位
  • 前端高频面试题day3