JavaScript Promise 的 then() 方法详解

JavaScript Promise 的 then() 方法详解

then() 是 Promise 的核心方法,用于处理异步操作的成功或失败结果,并支持链式调用。以下是其定义、参数、使用方式、注意事项及用例说明。


一、then() 方法定义

语法:

promise.then(
  onFulfilled?: (value: T) => TResult | Promise<TResult>,
  onRejected?: (reason: any) => TError | Promise<TError>
): Promise<TResult | TError>

作用:

  • 接收两个回调函数(onFulfilledonRejected),分别处理 Promise 的成功和失败状态
  • 返回一个新的 Promise,其状态由回调函数的执行结果决定

二、参数说明

1. onFulfilled(可选)

  • 触发条件:当 Promise 状态变为 fulfilled(成功)时调用
  • 输入参数value(Promise 的解决值)
  • 返回值:可以是任意值或另一个 Promise,影响新 Promise 的状态和结果

2. onRejected(可选)

  • 触发条件:当 Promise 状态变为 rejected(失败)时调用
  • 输入参数reason(Promise 的拒绝原因)
  • 返回值:可以是任意值或另一个 Promise,用于恢复链式调用

参数默认行为

  • 如果忽略 onFulfilled,则成功值直接传递给下一个 .then()
  • 如果忽略 onRejected,则错误会沿链向下传递,直到被 .catch() 捕获

三、使用方式

1. 基本用法

Promise.resolve(10)
  .then((value) => {
    console.log(value); // 输出 10
    return value * 2;
  })
  .then((result) => {
    console.log(result); // 输出 20
  });

2. 错误处理

Promise.reject("Error!")
  .then(
    (value) => console.log("不会执行"), 
    (reason) => {
      console.error(reason); // 输出 "Error!"
      return "Recovered";
    }
  )
  .then((result) => {
    console.log(result); // 输出 "Recovered"
  });

3. 链式调用中的异步操作

function asyncAdd(a, b) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(a + b), 1000);
  });
}

asyncAdd(2, 3)
  .then((sum) => asyncAdd(sum, 5))
  .then((final) => console.log(final)); // 输出 10(2秒后)

四、注意事项

1. 错误冒泡

如果未提供 onRejected 或未用 .catch(),错误会一直传递到链的末端:

Promise.reject("Error")
  .then((value) => console.log("Success"))
  .catch((err) => console.error(err)); // 必须捕获

2. 返回值类型

  • 返回普通值:新 Promise 直接成功
  • 返回 Promise:新 Promise 跟随其状态
  • 返回 thenable 对象:展开后生成新 Promise
Promise.resolve(1)
  .then(() => ({
    then(resolve) {
      resolve("Thenable");
    }
  }))
  .then((result) => console.log(result)); // 输出 "Thenable"

3. 同步错误处理

then() 回调中抛出同步错误会自动触发拒绝:

Promise.resolve()
  .then(() => {
    throw new Error("Oops!");
  })
  .catch((err) => console.error(err)); // 捕获错误

4. 微任务队列

then() 的回调在微任务队列中执行,优先于宏任务:

setTimeout(() => console.log("Macro Task"), 0);
Promise.resolve().then(() => console.log("Micro Task"));
// 输出顺序:Micro Task → Macro Task

五、用例说明

用例 1:链式数据处理

function fetchUser() {
  return Promise.resolve({ id: 1, name: "Alice" });
}

fetchUser()
  .then((user) => {
    console.log("User:", user);
    return user.name.toUpperCase();
  })
  .then((name) => {
    console.log("Name:", name); // "ALICE"
  });

用例 2:组合多个异步操作

function fetchOrder(userId) {
  return Promise.resolve([{ id: 100, product: "Book" }]);
}

function fetchPayment(orderId) {
  return Promise.resolve({ amount: 50 });
}

fetchUser()
  .then((user) => fetchOrder(user.id))
  .then((orders) => fetchPayment(orders[0].id))
  .then((payment) => console.log("Payment:", payment.amount)) // 50
  .catch((err) => console.error("Chain failed:", err));

用例 3:错误恢复

Promise.reject("Network Error")
  .then(null, (reason) => {
    console.log("Fallback to cached data");
    return { data: "Cached Data" };
  })
  .then((data) => {
    console.log("Data:", data.data); // "Cached Data"
  });

总结

  • then() 是 Promise 链式调用的核心:处理成功/失败状态,返回新 Promise
  • 参数为函数:非函数参数会被忽略,可能导致意外结果
  • 错误处理优先:始终用 .catch()onRejected 处理错误
  • 返回值决定链式状态:理解不同返回值类型的影响
  • 微任务机制:回调在微任务队列执行,优先于宏任务
<think>首先,用户的问题是:“我想比较CompletableFuture的then方法和when方法 请问CompletableFuture的then方法和when方法的比较”。所以,我需要重点讨论CompletableFuture的then方法和when方法的区别。 回顾系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 用户提供了几个引用内容: - [^1]:讨论了CompletableFuture替代Future+Callable,提到then和when操作防止阻塞和轮询。 - [^2]:介绍了CompletionStage接口,CompletableFuture是其实现类。 - [^3]:与[^1]类似,强调使用then和when操作防止阻塞。 - [^4]:解释了CompletableFuture的构造方式。 - [^5]:描述了supplyAsync和runAsync方法。 用户在上一个问题中说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。”但在对话历史中,没有显示上一次的问题和回答。系统示例是关于快速排序的,但用户当前是关于CompletableFuture的。 在用户消息中,提到“参考站内引用”,但这不是真实的引用;它是模拟的引用用于上下文。用户说“上述引用仅供参考”,所以我应该基于真实知识回答,但参考这些提供的引用。 关键点:用户想比较then和when方法。 在Java的CompletableFuture中: - then方法:通常指的是thenApply, thenAccept, thenRun等方法,用于添加后续动作。 - when方法:实际上,在CompletableFuture中,没有直接叫“when”的方法。用户可能误解了。常见的后缀是then,如thenCompose, thenCombine等。 检查用户的引用: - [^1]说:“使用completableFuture的使用就可以用then,when等等操作” - [^3]说:“使用completableFuture的使用就可以用then,when等等操作来防止以上的阻塞和轮询isDone的现象出现” 但在标准的CompletableFuture API中,我没有找到名为“when”的方法。可能用户指的是“whenComplete”方法。 回想CompletableFuture的方法: - then系列:thenApply, thenAccept, thenRun, thenCompose, etc. 这些是用于转换结果或添加后续操作的。 - whenComplete:用于添加一个动作,无论任务成功或失败,都会执行,通常用于清理或日志。 还有一个是“handle”,类似于whenComplete,但可以转换结果。 在引用[^5]中,提到“同步获取结果”,但没有具体说when方法。 可能用户在引用中看到的“when”是一个泛称,或者可能是指“whenComplete”。 在[^1]和[^3]中,都说“then,when等等操作”,所以可能“when”指的是“whenComplete”或类似。 为了准确回答,我应该澄清: - then方法:指的是像thenApply这样的方法,用于处理正常结果。 - when方法:可能指的是whenComplete方法,用于处理完成状态(无论成功或失败)。 在CompletableFuture中,常见的方法是: - thenApply(Function<? super T,? extends U> fn):转换结果。 - thenAccept(Consumer<? super T> action):消费结果。 - thenRun(Runnable action):运行一个动作。 - whenComplete(BiConsumer<? super T, Throwable> action):当完成时,执行动作,参数是结果和异常。 - handle(BiFunction<? super T, Throwable, ? extends U> fn):处理结果和异常。 所以,比较thenApply(代表then系列)和whenComplete。 用户说“then方法和when方法”,所以我会比较thenApply和whenComplete作为示例。 回答结构: 1. 解释CompletableFuture的背景(基于引用)。 2. 描述then方法(如thenApply)。 3. 描述when方法(如whenComplete)。 4. 比较两者的区别:关键点如处理正常结果 vs. 处理完成状态(包括异常)。 5. 使用中文。 6. 在引用内容末尾添加引用标识。 7. 生成相关问题。 添加引用:用户提供了[^1]到[^5],所以我应该在回答中引用它们。 例如: - 基于[^1],CompletableFuture可以防止阻塞。[^1] 使用LaTeX:用户的问题没有数学表达式,所以可能不需要。但如果有,比如在比较中,可以用数学表达式来表示,但在这里可能不必要。指令说“所有行内数学表达式必须使用$...$格式”,但如果不需要,就不用了。 生成相关问题:在回答结束后,添加相关问题。 回答组织: - 介绍CompletableFuture。 - 解释then方法和when方法。 - 比较它们的区别。 - 总结。 - 相关问题。 确保回答真实可靠:基于Java文档和知识。 </think>### CompletableFuture的then方法与when方法比较 CompletableFuture是Java 8引入的异步编程工具,基于CompletionStage接口实现,用于处理异步任务链式操作。它可以替代传统的Future和Callable,避免阻塞(如get方法)和轮询(如isDone方法),从而提升并发性能[^1][^3]。在CompletableFuture中,“then方法(如thenApply、thenAccept)和“when”方法(常见指whenComplete)是核心操作,用于添加后续动作。但用户需注意:**标准API中没有直接命名为“when”的方法**;您可能在引用中看到的“when”是一个泛称,实际指的是whenComplete方法(用于处理完成状态,包括正常结果或异常)[^1][^3]。下面我将基于引用内容,逐步比较thenApply(代表then系列)和whenComplete作为示例,确保解释清晰可靠。 #### 1. **then方法详解(以thenApply为例)** - **功能**:thenApply是CompletionStage接口的方法,用于在异步任务正常完成后,对结果进行转换或处理。它只关注任务的**成功状态**,如果任务失败,则不会触发该操作[^2][^4]。 - **语法**:`thenApply(Function<? super T,? extends U> fn)`,其中: - `T` 是输入类型(前一个任务的结果)。 - `U` 是输出类型(处理后生成的新结果)。 - 操作是异步的,默认使用ForkJoinPool执行[^5]。 - **示例场景**:将一个字符串转换为大写。 ```java CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello"); CompletableFuture<String> result = future.thenApply(str -> str.toUpperCase()); result.thenAccept(System.out::println); // 输出 "HELLO" ``` 在这个例子中,thenApply接收前一个任务的结果("hello"),应用一个函数(转为大写),并返回一个新的CompletableFuture[^4][^5]。 **关键点**: - 只处理正常结果,忽略异常。 - 支持链式调用(如thenApply后跟thenAccept)。 - 类似于函数式编程的map操作。 #### 2. **when方法详解(以whenComplete为例)** - **功能**:whenComplete是CompletionStage接口的方法,用于在异步任务完成时(无论成功还是失败)执行一个动作。它关注任务的**完整状态**,包括结果和异常,但不会改变结果本身(仅用于日志或清理)[^2][^5]。 - **语法**:`whenComplete(BiConsumer<? super T, Throwable> action)`,其中: - `T` 是结果类型。 - `Throwable` 是异常对象(如果任务失败)。 - 操作在任务完成后立即执行,可以是同步或异步。 - **示例场景**:记录任务完成状态,包括异常。 ```java CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { if (Math.random() > 0.5) throw new RuntimeException("error"); return "world"; }); future.whenComplete((result, ex) -> { if (ex != null) System.err.println("Task failed: " + ex.getMessage()); else System.out.println("Task succeeded: " + result); }); ``` 在这个例子中,whenComplete处理所有完成情况:如果任务失败,打印错误;成功则打印结果[^3][^5]。 **关键点**: - 处理所有状态(成功或失败),但不修改结果。 - 常用于资源释放或监控,而不是数据转换。 - 类似的方法有handle(可转换结果和异常)。 #### 3. **then方法与when方法的关键比较** 以下基于功能、行为和应用场景进行对比,总结主要区别(参考Java文档和引用内容): | 特征 | thenApply(then系列代表) | whenComplete(when系列代表) | |---------------|---------------------------|------------------------------| | **核心目的** | 处理正常结果,进行转换或消费(如map操作) | 处理完成状态(成功或失败),用于副作用(如日志或清理) | | **异常处理** | 如果前一个任务失败,则不执行;异常会传播到后续操作 | 无论任务成功或失败,都会执行;异常不会传播,仅用于读取 | | **结果影响** | 返回一个新的CompletableFuture,可以改变结果类型(例如,从String到Integer) | 返回原CompletableFuture的同类型结果,不改变结果值(仅执行动作) | | **异步执行** | 默认异步(使用ForkJoinPool),但可选指定Executor[^5] | 通常在任务完成线程执行(同步),但可通过async后缀(如whenCompleteAsync)实现异步 | | **典型用例** | 任务链式转换(例如,从API获取数据后解析JSON) | 状态监控或资源清理(例如,无论成败都关闭数据库连接)[^1][^3] | | **数学表示** | 设$f: T \to U$,则thenApply应用$f$,输出$U$值 | 设$g: (T, Throwable) \to void$,则whenComplete执行$g$,无输出变化 | **总结区别**: - **thenApply优先用于业务逻辑链**:当您需要基于前一个结果继续计算时(如thenApply后跟thenAccept),它构建了一个“成功路径”的流水线[^4][^5]。 - **whenComplete优先用于错误恢复或监控**:当您不关心结果转换,但需确保任务完成时执行某些动作(如记录日志或释放资源),它提供全局状态处理[^2][^3]。 - 实际开发中,两者常结合使用:例如,使用thenApply处理正常数据,再用whenComplete添加异常回退。 #### 4. **最佳实践建议** - 避免混淆:确保代码中明确使用标准API名称(如thenApply或whenComplete),而非泛称“when”方法,以减少误解[^1][^3]。 - 性能考虑:所有异步操作默认使用ForkJoinPool,但高并发场景下,建议指定自定义Executor优化线程管理[^5]。 - 异常处理组合:使用handle替代whenComplete,如果需同时处理异常并转换结果。例如: ```java future.handle((result, ex) -> ex != null ? "default" : result.toUpperCase()); ``` 基于以上引用和分析,CompletableFuture的then系列和whenComplete方法各有侧重:then方法专注于结果转换的链式操作,而when方法(即whenComplete)专注于状态无关的副作用处理。正确选择能显著提升异步代码的可读性和健壮性[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值