Interoperability with Promise
ECMAScript 6 and Promise Interoperability
ECMAScript 6 introduced Promises as a standard way to handle asynchronous operations, deeply integrating them with other ES6 features. Promises not only solve the "callback hell" problem but also form a complete asynchronous solution alongside generators, async/await, and other features.
Promise Basics and ES6 Syntax Integration
Promises are a native constructor provided by ES6, representing the eventual completion or failure of an asynchronous operation. Their basic usage is tightly integrated with ES6 features like arrow functions and destructuring:
const fetchData = () => new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
success ? resolve({ status: 200, data: 'Sample' }) :
reject(new Error('Request failed'));
}, 1000);
});
// ES6 arrow function and destructuring
fetchData()
.then(({ data }) => console.log(data))
.catch(error => console.error(error.message));
The three states of a Promise (pending, fulfilled, rejected) also work well with ES6's module system:
// Exporting a Promise in a module
export const apiPromise = fetchData();
// Importing in another module
import { apiPromise } from './api';
apiPromise.then(handleResponse);
Promise and Iterator/Generator Interoperability
ES6 generator functions can be combined with Promises for more elegant asynchronous flow control:
function* asyncGenerator() {
try {
const result1 = yield fetchData();
const result2 = yield fetchData();
return [result1, result2];
} catch (error) {
console.error('Generator error:', error);
}
}
// Helper function to execute the generator
function runGenerator(gen) {
const iterator = gen();
function iterate(iteration) {
if (iteration.done) return iteration.value;
const promise = iteration.value;
return promise.then(
x => iterate(iterator.next(x)),
err => iterate(iterator.throw(err))
);
}
return iterate(iterator.next());
}
runGenerator(asyncGenerator).then(console.log);
This pattern later evolved into the async/await
syntactic sugar, which is still based on Promises and generators under the hood.
Promise Static Methods and ES6 Features
ES6 provides several static methods for Promises that work in harmony with new language features:
// Promise.all with destructuring assignment
const [user, posts] = await Promise.all([
fetch('/user'),
fetch('/posts')
]);
// Promise.race with template literals
Promise.race([
fetchData(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timeout after 3000ms`)), 3000))
]).catch(err => console.log(`${err.message} occurred`));
// Promise.allSettled with object spreading
const results = await Promise.allSettled(requests);
const successful = results.filter(({status}) => status === 'fulfilled');
Promise Prototype Methods and ES6 Classes
Using Promises in ES6 classes enables the creation of more powerful asynchronous objects:
class AsyncQueue {
constructor() {
this.queue = [];
this.resolveCurrent = null;
this.currentPromise = null;
}
enqueue(item) {
if (this.resolveCurrent) {
this.resolveCurrent(item);
this.resolveCurrent = null;
} else {
this.queue.push(item);
}
}
dequeue() {
if (this.queue.length > 0) {
return Promise.resolve(this.queue.shift());
}
return new Promise(resolve => {
this.resolveCurrent = resolve;
});
}
}
// Usage example
const queue = new AsyncQueue();
queue.dequeue().then(console.log); // Waits for data
setTimeout(() => queue.enqueue('Hello'), 1000);
Promise and Proxy Integration
ES6's Proxy can enhance Promise behavior:
const loggingProxy = (promise) => new Proxy(promise, {
get(target, prop) {
if (prop === 'then') {
return (...args) => {
console.log('Then handler attached');
return target.then(...args);
};
}
return target[prop];
}
});
const proxiedPromise = loggingProxy(fetchData());
proxiedPromise.then(() => {}); // Logs "Then handler attached"
Promise and Reflect Metaprogramming
The Reflect API can be combined with Promises for advanced metaprogramming patterns:
function timeoutPromise(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms));
return Reflect.apply(Promise.race, null, [promise, timeout]);
}
// Usage example
timeoutPromise(fetchData(), 500)
.then(console.log)
.catch(err => console.error(err.message));
Promise and Symbol Collaboration
ES6's Symbol type can be used to customize Promise behavior:
const kCustomThen = Symbol('customThen');
class CustomPromise extends Promise {
[kCustomThen](onFulfilled, onRejected) {
console.log('Custom then called');
return this.then(onFulfilled, onRejected);
}
}
const cp = new CustomPromise(resolve => resolve('value'));
cp[kCustomThen](value => console.log(value));
Special Applications of Promises in the Module System
Top-level await
in ES6 modules is essentially syntactic sugar over Promises:
// module.mjs
const data = await fetchData(); // Top-level await in modules
export { data };
// Equivalent to
const dataPromise = fetchData();
export const data = dataPromise;
Performance Considerations and ES6 Optimizations
Modern JavaScript engines have special optimizations for Promises:
// V8 engine optimizations for microtasks
Promise.resolve().then(() => console.log('microtask 1'));
queueMicrotask(() => console.log('microtask 2'));
// Avoiding Promise constructor antipatterns
// Antipattern
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Better approach
function delay(ms) {
return Promise.resolve().then(() =>
new Promise(resolve => setTimeout(resolve, ms)));
}
Promise and Web API Integration
Browser Web APIs increasingly return Promise objects:
// Modern Web API example
async function useModernApis() {
const clipboardItems = await navigator.clipboard.read();
const battery = await navigator.getBattery();
const storage = await navigator.storage.estimate();
return { clipboardItems, battery, storage };
}
// Using ES6 object shorthand
useModernApis().then(({ battery }) =>
console.log(`Battery level: ${battery.level * 100}%`));
Promise Patterns and Antipatterns
Best practices for Promises with ES6 features:
// Correct chaining
fetchData()
.then(data => processData(data))
.then(processed => saveData(processed))
.catch(handleError);
// Antipattern: Nested Promises
fetchData().then(data => {
processData(data).then(processed => {
saveData(processed).then(() => {
// More nesting
});
});
});
// Better parallel processing
const [user, orders] = await Promise.all([
fetchUser(),
fetchOrders()
]);
Promise Extensions and Composition
Extending Promise functionality using ES6 classes:
class CancellablePromise extends Promise {
constructor(executor) {
super((resolve, reject) => {
executor(resolve, reject, () => {
reject(new Error('Cancelled'));
});
});
this._cancel = null;
}
cancel() {
this._cancel?.();
}
static withCancel(executor) {
let cancelFn;
const promise = new CancellablePromise((res, rej, cancel) => {
cancelFn = cancel;
return executor(res, rej);
});
promise._cancel = cancelFn;
return promise;
}
}
// Usage example
const cp = CancellablePromise.withCancel((resolve, reject) => {
const timer = setTimeout(() => resolve('Done'), 2000);
return () => clearTimeout(timer);
});
setTimeout(() => cp.cancel(), 1000);
cp.catch(err => console.log(err.message)); // Logs "Cancelled"
Promise and Type Systems
Using ES6 Promise type definitions in TypeScript:
interface ApiResponse<T> {
data: T;
status: number;
}
function typedFetch<T>(url: string): Promise<ApiResponse<T>> {
return fetch(url).then(res => res.json());
}
// Usage example
typedFetch<User[]>('/users')
.then(({ data }) => data.map(user => user.name))
.catch((err: Error) => console.error(err.stack));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:top-level await
下一篇:常见异步模式实现