自学内容网 自学内容网

JS异步编程进阶(一):Callback、Promise、Async/Await 和 Observable 深度对比

在现代 JavaScript 编程中,异步操作是常见且必不可少的部分。处理异步的方式多种多样,其中最常见的有 Callback、Promise、Async/Await,以及近年来随着响应式编程(Reactive Programming)理念兴起的 Observable。本文将对这几种异步处理方式进行对比,帮助你理解它们各自的优缺点,以及在实际开发中如何选择合适的方式。

本文将介绍一种非常适合js这种基于事件的设计模式-观察者模式,以及尤其衍生的响应式编程。因为tc39到WICG等变化,导致迟迟未能推荐 Obserbvable提案最新 老版tc39提案,至于这些团体事件我们不管,因为js是很灵活的,有一个叫做 rxjs 的库较好的实现了这个模式,而且出到了第七个大版本。个人也使用其超过三年半
后续的一些内容,尤其是较为复杂的业务中,往往需要用到它来简化异步控制流,所以这里先介绍和对比集中场景的解决方案

1. Callback

概述

Callback 是 JavaScript 中最原始的异步编程方式。通过将函数作为参数传递给另一个函数,异步操作完成时,调用传入的回调函数来执行后续逻辑。通常会在事件处理、定时器或网络请求中使用。

示例

function fetchData(callback) {
    setTimeout(() => {
        callback(null, "Data fetched");
    }, 1000);
}

fetchData((err, result) => {
    if (err) {
        console.error(err);
    } else {
        console.log(result);  // "Data fetched"
    }
});

优点

  • 语法简单,容易理解。
  • 可以直接处理多个并发异步操作。

缺点

  • 容易导致回调地狱(Callback Hell):当多个异步操作需要按顺序执行时,回调函数嵌套过深,代码难以维护和阅读。
  • 错误处理复杂:回调函数中每次都需要手动处理错误,容易遗漏。

2. Promise

概述

Promise 是为了解决 Callback 方式中代码难以维护的问题而引入的。它表示一个异步操作的最终完成(或失败),并返回一个结果。通过 .then().catch() 来链式处理异步任务和错误。

示例

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data fetched");
        }, 1000);
    });
}

fetchData()
    .then(result => console.log(result))  // "Data fetched"
    .catch(err => console.error(err));

优点

  • 链式结构清晰,避免了回调地狱,异步流程更具可读性。
  • 内建错误处理机制,.catch() 可以集中处理错误。
  • 提供了 Promise.all() 等辅助函数,可以方便处理并发任务。

缺点

  • 链式调用过多时,代码仍可能变得冗长,尤其是在复杂场景下。
  • 对比 async/await,Promise 仍需要较多的手动处理和拆解。

3. Async/Await

概述

Async/Await 是基于 Promise 的语法糖,提供了更加同步的写法来处理异步操作。async 关键字将函数声明为异步函数,而 await 用于等待一个 Promise 完成,显著提高了代码的可读性。

示例

async function fetchData() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("Data fetched");
        }, 1000);
    });
}

async function processData() {
    try {
        const result = await fetchData();
        console.log(result);  // "Data fetched"
    } catch (err) {
        console.error(err);
    }
}

processData();

优点

  • 语法简洁:异步代码看起来像同步代码,易读易写。
  • 错误处理简单:可以使用 try...catch 进行错误捕获,和同步代码的错误处理保持一致。
  • 更容易处理复杂的异步逻辑:通过 await,可以线性地编写代码,而不必嵌套回调或 .then()

缺点

  • 需要浏览器支持或 Babel 等编译工具进行转译。
  • 不能轻易取消已启动的异步任务(例如 HTTP 请求)。
  • 虽然处理流程简单,但对于更复杂的异步流操作,表现仍不如 RxJS 中的 Observable。

4. Observable(RxJS)

概述

Observable 是 RxJS(Reactive Extensions for JavaScript)库中的核心概念,是一种更高级的异步处理方式。与 Promise 只能处理一次性结果不同,Observable 可以处理多个值,并且可以流式地响应异步事件。Observable 更像是一种可以多次发出值的流,可以用来处理时间流(Event Streams)、多次异步请求等。

示例

import { Observable } from 'rxjs';

const observable = new Observable(subscriber => {
    setTimeout(() => {
        subscriber.next('Data 1');
        subscriber.next('Data 2');
        subscriber.complete();
    }, 1000);
});

observable.subscribe({
    next(value) { console.log(value); },  // "Data 1", "Data 2"
    complete() { console.log('Complete'); }
});

优点

  • 多值处理:可以像数据流一样处理多次值的发射,而不仅仅是一个值。
  • 取消异步操作:可以通过 .unsubscribe() 来取消正在进行的异步任务,Promise 没有这种能力。
  • 响应式编程:Observable 支持对事件流的操作符(例如 mapfiltermerge 等),可以轻松实现复杂的事件组合和处理。
  • 支持多种异步操作的高级控制,如并发、节流、去抖等,尤其适合事件流、大数据流处理等场景。

缺点

  • 学习曲线较陡,需要了解响应式编程(Reactive Programming)的基本概念。
  • 如果不使用 RxJS,浏览器原生不支持 Observable,需要额外引入库。

5. 关键点对比

特性CallbackPromiseAsync/AwaitObservable
语法复杂度简单中等简单中等
可读性差(回调地狱)最佳中等
错误处理复杂易于链式处理简单(try/catch)强大(catch,retry)
一次性结果处理是,且可处理多个值
取消异步任务
高级异步控制中等中等强大
并发处理通过手动实现Promise.all()通过多次 await 实现内建多种并发控制
过程管理容易
框架集成简单简单简单中等

6. 使用场景总结

  • Callback:适合简单的异步任务,但当异步操作复杂时,推荐使用 Promise 或 Async/Await 来避免回调地狱。
  • Promise:适合处理单个异步操作,特别是当需要链式处理多个异步任务时。
  • Async/Await:适合大多数场景,特别是需要同步风格代码的异步任务,代码简洁且易于维护。
  • Observable:适合需要处理多值异步流、响应事件、复杂异步任务的场景,尤其是需要高级控制(如取消、并发、错误重试)时。

结论

在 JavaScript 中,处理异步操作的方式随着技术的发展逐步演进。Callback 作为最基本的异步处理方法,在简单场景下仍然可用,但容易导致回调地狱。Promise 和 Async/Await 提供了更清晰的语法来处理异步任务,尤其是 Async/Await,可以写出几乎像同步代码一样简洁的异步代码。而 Observable 则为复杂的异步流控制提供了强大的工具,尤其适合那些需要高灵活性和事件流处理的应用场景。

理解并掌握这些异步处理方式,可以帮助我们在不同场景下选择最合适的工具,提高代码的可读性、可维护性和扩展性。

涉及到复杂的异步控制场景 - 例如持续执行的任务 ,非一次性必须完成的任务,推荐无脑rxjs,欢迎继续阅读下一篇应用文章 rxjs与Vue、React、Angular框架集成及跨框架状态管理实现原理,了解实践中如何与各个框架集成


原文地址:https://blog.csdn.net/m0_38015699/article/details/142909358

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!