Improved asynchronous programming support
The Evolution of Asynchronous Programming
ECMAScript 15 introduced significant improvements to asynchronous programming support, offering more concise and powerful syntax and APIs. These enhancements allow developers to handle asynchronous operations more easily, reduce callback hell, and improve code readability and maintainability. From Promise enhancements to new asynchronous iterators, ECMAScript 15 brings more possibilities to modern JavaScript development.
Promise Enhancements
ECMAScript 15 introduced several improvements to Promises, making them more powerful and user-friendly. The new Promise.withResolvers()
method allows developers to create and control Promises more flexibly.
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => {
resolve('Operation succeeded');
}, 1000);
promise.then(value => {
console.log(value); // "Operation succeeded"
});
This new method is particularly useful for scenarios where the resolution control of a Promise needs to be passed to other functions or modules. Compared to the traditional new Promise()
approach, it provides a cleaner code structure.
Asynchronous Iterator Improvements
ECMAScript 15 enhanced support for asynchronous iterators, making it more intuitive to handle asynchronous data streams. New methods like AsyncIterator.prototype.map()
and AsyncIterator.prototype.filter()
simplify asynchronous data transformations.
async function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
const doubled = generateNumbers().map(async x => x * 2);
(async () => {
for await (const num of doubled) {
console.log(num); // 2, 4, 6
}
})();
These new methods reduce boilerplate code for common asynchronous iteration operations, improving development efficiency.
Standardization of Top-Level Await
Although top-level await
was already available in some environments, ECMAScript 15 formally standardized it. This means await
can now be used directly at the module level without being wrapped in an async function.
// Module code
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
This improvement simplifies module initialization code, especially in scenarios requiring configuration loading or resource initialization.
More Robust Error Handling
ECMAScript 15 improved asynchronous error handling with the addition of the Promise.try()
method, providing a more elegant way to handle both synchronous and asynchronous errors.
function mightFail() {
if (Math.random() > 0.5) {
throw new Error('Synchronous error');
}
return Promise.reject('Asynchronous error');
}
Promise.try(() => mightFail())
.catch(error => {
console.error('Caught error:', error);
});
This approach unifies the handling of synchronous and asynchronous errors, avoiding the issue of forgetting to wrap synchronous code in try-catch
within async functions.
Asynchronous Context Tracking
ECMAScript 15 introduced the Asynchronous Context Tracking API, helping developers better understand and debug complex asynchronous code flows. The new AsyncContext
class allows context information to be preserved across asynchronous operations.
const context = new AsyncContext('Default value');
async function task() {
console.log(context.get()); // Get current context value
}
async function main() {
context.run('Task 1 context', async () => {
await task(); // Output: "Task 1 context"
});
context.run('Task 2 context', async () => {
await task(); // Output: "Task 2 context"
});
}
main();
This is particularly useful for tracking request chains in distributed systems or implementing asynchronous logging.
Performance Optimizations
ECMAScript 15 optimized the underlying implementation of asynchronous operations, particularly improving the efficiency of microtask queue processing. The new queueMicrotask()
function provides a more efficient way to schedule microtasks.
function processData(data) {
// Process data in a microtask
queueMicrotask(() => {
console.log('Processing data:', data);
});
}
Compared to the Promise.resolve().then()
approach, queueMicrotask()
has lower overhead, making it suitable for high-performance scenarios.
Asynchronous Resource Management
The new AsyncDisposable
interface and using
declaration provide a standardized mechanism for asynchronous resource management, similar to C#'s using
statement or Python's async with
.
class DatabaseConnection {
async [Symbol.asyncDispose]() {
await this.close();
}
}
{
await using db = new DatabaseConnection();
// Use the database connection
await db.query('SELECT * FROM users');
} // Automatically calls asyncDispose method
This pattern ensures asynchronous resources (e.g., database connections, file handles) are properly released, even if an exception occurs.
Composable Asynchronous Operations
ECMAScript 15 introduced an enhanced version of Promise.allSettled()
and new composition methods, making it more flexible to coordinate multiple asynchronous operations.
const results = await Promise.allSettled([
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
]);
const successful = results
.filter(p => p.status === 'fulfilled')
.map(p => p.value);
These improvements allow developers to handle the results of parallel asynchronous operations with greater precision.
Asynchronous Generator Pipelines
The new asynchronous generator pipeline operator |>
makes composing multiple asynchronous processing steps more intuitive and concise.
async function* source() {
yield 1; yield 2; yield 3;
}
async function* transform(iter) {
for await (const x of iter) {
yield x * 2;
}
}
async function* logger(iter) {
for await (const x of iter) {
console.log(x);
yield x;
}
}
const pipeline = source() |> transform |> logger;
(async () => {
for await (const x of pipeline) {
// Already transformed and logged
}
})();
This functional programming style makes asynchronous data stream processing more declarative and modular.
Timer API Improvements
ECMAScript 15 extended setTimeout
and setInterval
with Promise-returning versions, making them easier to use in modern asynchronous code.
// Wait for 1 second
await setTimeout(1000);
console.log('Executed after 1 second');
// Version with return value
const result = await setTimeout(1000, 'Return value');
console.log(result); // "Return value"
These improvements better integrate traditional timer APIs with modern asynchronous programming patterns.
Asynchronous Testing Support
For testing scenarios, ECMAScript 15 added specialized asynchronous assertions and utility functions, simplifying the testing of asynchronous code.
import { asyncAssert } from 'assert';
async function fetchData() {
return { id: 1, name: 'Test data' };
}
await asyncAssert.doesNotReject(fetchData());
await asyncAssert.rejects(Promise.reject(new Error('Expected error')));
These tools make writing asynchronous test cases simpler and more reliable.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn