在现代的JavaScript开发中,处理异步任务变得愈发重要,因为它们允许我们在等待I/O、网络请求或定时器等事件时继续执行其他任务,以提高程序的性能和响应能力。本文将介绍JavaScript中异步编程的演变过程,从最初的回调地狱到后来的Promise和Async/Await。
回调地狱
回调地狱是一种异步编程的反模式,它在嵌套的回调函数中处理多个异步任务,导致代码难以阅读和维护。以下是一个简单的回调地狱演示:
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是一种用于处理异步任务的对象,它代表了一个异步操作的最终完成或失败,并提供了更优雅的解决方案,避免了回调地狱的问题。
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还提供了异常捕获机制,使得我们可以方便地处理异步任务的错误。
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完成时就终止。
// 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引入的异步编程语法糖,使得异步代码更加像同步代码一样,进一步提高了可读性。
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.allSettled
和 Promise.any
。
Promise.allSettled
(ES2020)
在ES2020中,JavaScript引入了 Promise.allSettled
方法,它用于处理多个Promise,无论它们是被解决(fulfilled)还是被拒绝(rejected)。
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结果的对象数组,每个对象都包含 status
和 value
或 reason
属性。
这使得我们能够轻松地处理多个Promise的状态,而不必担心其中一个Promise被拒绝而导致整个操作失败。
Promise.any
(ES2021)
在ES2021中,JavaScript引入了 Promise.any
方法,它用于处理多个Promise,只要其中至少有一个被解决。
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.allSettled
和Promise.any
有所帮助。