解析 JavaScript 异步编程:从回调地狱到 Promise 和 Async/Await

在现代的JavaScript开发中,处理异步任务变得愈发重要,因为它们允许我们在等待I/O、网络请求或定时器等事件时继续执行其他任务,以提高程序的性能和响应能力。本文将介绍JavaScript中异步编程的演变过程,从最初的回调地狱到后来的Promise和Async/Await。

回调地狱

回调地狱是一种异步编程的反模式,它在嵌套的回调函数中处理多个异步任务,导致代码难以阅读和维护。以下是一个简单的回调地狱演示:

typescript 复制代码
function add(a: number, b: number, callback: (res: number) => void): void  {
    setTimeout(() => {
        callback(a + b);
    }, 1000);
}

// 回调地狱示例
add(2, 3, res => {
    console.log('2 + 3 = ', res);
    add(res, 4, res => {
        console.log('2 + 3 + 4 = ', res);
        add(res, 5, res => {
            console.log('2 + 3 + 4 + 5 = ', res);
        });
    });
});

如上所示,每个异步任务的结果都在回调函数中处理,形成了嵌套的结构,使得代码难以理解。

Promise

Promise是一种用于处理异步任务的对象,它代表了一个异步操作的最终完成或失败,并提供了更优雅的解决方案,避免了回调地狱的问题。

typescript 复制代码
async function add2(a: number, b: number): Promise<number>  {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(a + b);
        }, 2000);
    });
}

// Promise链式调用
add2(2, 3)
    .then((res: number )=> {
        console.log('2 + 3 = ', res);
        return add2(res, 4);
    })
    .then((res: number )=>{
        console.log('2 + 3 + 4 = ', res);
        return add2(res, 4);
    })
    .then((res: number) => {
        console.log('result:', res);
    })
    .catch(e => console.error(`ERROR: ${e}`));

通过Promise,我们可以将异步任务按顺序组织在链式调用中,使得代码更具可读性。

Promise的异常捕获

Promise还提供了异常捕获机制,使得我们可以方便地处理异步任务的错误。

typescript 复制代码
add2(2, 3)
    .then((x: number )=> Promise.reject(10))
    .then((x: number )=> add2(x, 6))
    .then((res: number) => console.log(res))
    .catch(e => console.error(`ERROR: ${e}`));

在这个例子中,当第一个Promise返回了一个拒绝状态时,异常会被捕获,而不会继续执行后续的Promise。

Promise的组合

Promise.all和Promise.race是Promise的两个重要方法。Promise.all等待所有Promise都完成,而Promise.race在第一个Promise完成时就终止。

typescript 复制代码
// Promise同时等待后做事情
Promise.all([add2(2, 3), add2(4, 5)]).then(([a , b]) => {
    console.log('result', a, b);
    return mul(a, b);
}).then((res: number) =>
    console.log('result:', res)
);

// Promise谁先做事情就用谁
Promise.race([add2(2, 3), mul(4, 5)]).then((res: number) =>
    console.log('result:', res)
);

这两个方法使我们能够更灵活地组合和控制多个异步任务的执行。

Async/Await

Async/Await是ES2017引入的异步编程语法糖,使得异步代码更加像同步代码一样,进一步提高了可读性。

typescript 复制代码
async function main(): Promise<void> {
    try {
        const result1 = await add2(2, 3);
        console.log('2 + 3 = ', result1);

        const result2 = await add2(result1, 4);
        console.log('2 + 3 + 4 = ', result2);

        const result3 = await add2(result2, 5);
        console.log('2 + 3 + 4 + 5 = ', result3);
    } catch (error) {
        console.error(`ERROR: ${error}`);
    }
}

main();

使用Async/Await,我们可以更清晰地表达异步任务的执行顺序,避免了回调地狱和过多的Promise链。

总的来说,JavaScript异步编程的演进经历了从回调地狱到Promise和最终的Async/Await的过程,这些新的语法和模式为开发者提供了更强大和清晰的工具,使得异步代码更易于编写和维护。希望本文对你理解JavaScript异步编程有所帮助。

JavaScript异步编程的最新进展:从Promise.allSettled到Promise.any

在ES2020和ES2021中,JavaScript引入了一些新的异步编程方法,进一步改善了处理Promise的体验。本文将介绍其中两个方法:Promise.allSettledPromise.any

Promise.allSettled(ES2020)

在ES2020中,JavaScript引入了 Promise.allSettled 方法,它用于处理多个Promise,无论它们是被解决(fulfilled)还是被拒绝(rejected)。

typescript 复制代码
function asyncFunction1(): Promise<string> {
    return new Promise(resolve => setTimeout(() => resolve('Result from asyncFunction1'), 1000));
}

function asyncFunction2(): Promise<number> {
    return new Promise(resolve => setTimeout(() => resolve(42), 500));
}

// 使用 allSettled 扩展方法
const promises = [asyncFunction1(), asyncFunction2()];

Promise.allSettled(promises).then(results => {
    results.forEach(result => {
        if (result.status === 'fulfilled') {
            console.log(`Fulfilled: ${result.value}`);
        } else {
            console.log(`Rejected: ${result.reason}`);
        }
    });
});

Promise.allSettled 返回一个新的Promise,该Promise在所有传入的Promise都已经被解决或被拒绝后才被解决。它返回一个包含每个Promise结果的对象数组,每个对象都包含 statusvaluereason 属性。

这使得我们能够轻松地处理多个Promise的状态,而不必担心其中一个Promise被拒绝而导致整个操作失败。

Promise.any(ES2021)

在ES2021中,JavaScript引入了 Promise.any 方法,它用于处理多个Promise,只要其中至少有一个被解决。

typescript 复制代码
function asyncFunction3(): Promise<string> {
    return new Promise(resolve => setTimeout(() => resolve('Result from asyncFunction3'), 1000));
}

function asyncFunction4(): Promise<number> {
    return new Promise((_, reject) => setTimeout(() => reject('Error from asyncFunction4'), 500));
}

// 使用 any 扩展方法
const promises2 = [asyncFunction3(), asyncFunction4()];

Promise.any(promises2)
    .then(result => {
        console.log(`Resolved: ${result}`);
        // 处理至少一个 Promise 被 resolved 的逻辑
    })
    .catch(errors => {
        console.error(`Rejected with all errors: ${errors}`);
        // 处理所有 Promise 都被 rejected 的逻辑
    });

Promise.any 返回一个新的Promise,该Promise在传入的Promise中至少有一个被解决后就被解决。它只要有一个Promise被解决,就会返回该Promise的解决值。

这个方法很有用,因为它提供了一种在多个异步任务中找到第一个成功解决的任务的方式。

这两个新方法为JavaScript开发者提供了更多处理异步任务的选择,使得我们能够更灵活地应对各种异步场景。希望这篇文章对你理解Promise.allSettledPromise.any有所帮助。

相关推荐
下雪天的夏风3 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom15 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan18 分钟前
electron react离线使用monaco-editor
javascript·react.js·electron
wn53119 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
^^为欢几何^^27 分钟前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
Hello-Mr.Wang32 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
救救孩子把35 分钟前
Java基础之IO流
java·开发语言
WG_1736 分钟前
C++多态
开发语言·c++·面试
宇卿.42 分钟前
Java键盘输入语句
java·开发语言
Amo Xiang1 小时前
2024 Python3.10 系统入门+进阶(十五):文件及目录操作
开发语言·python