1. 从回调地狱到Promise:前端异步编程的进化之路
记得刚入行前端那会儿,处理异步操作最痛苦的就是层层嵌套的回调函数。一个简单的数据获取流程可能写成这样:
javascript复制getUser(userId, function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
renderPage(user, posts, comments);
}, errorCallback);
}, errorCallback);
}, errorCallback);
这种"回调金字塔"不仅难以阅读和维护,错误处理更是噩梦。直到ES6引入Promise,我们才真正拥有了处理异步操作的优雅方案。
Promise本质上是一个表示异步操作最终完成或失败的对象。它有三种状态:
- pending:初始状态,既不是成功也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
这种状态机制让异步代码可以像同步代码一样线性书写:
javascript复制getUser(userId)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => renderPage(user, posts, comments))
.catch(error => console.error(error));
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 手写Promise核心实现
2.1 Promise基础结构
要实现一个Promise,我们需要考虑以下几个核心部分:
javascript复制class MyPromise {
constructor(executor) {
this.state = 'pending'; // 初始状态
this.value = undefined; // 成功时的值
this.reason = undefined; // 失败时的原因
this.onFulfilledCallbacks = []; // 成功回调队列
this.onRejectedCallbacks = []; // 失败回调队列
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 实现then方法
}
}
2.2 then方法的实现
then方法是Promise最核心的部分,它需要处理以下几种情况:
- 当前Promise已经完成或拒绝
- 当前Promise仍处于pending状态
- 回调函数返回普通值或新的Promise
javascript复制then(onFulfilled, onRejected) {
// 处理值穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
2.3 resolvePromise函数
这个函数用于处理then回调返回的值,确保符合Promise/A+规范:
javascript复制function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
注意:这里使用setTimeout是为了确保then回调总是异步执行,符合Promise/A+规范要求。这也是为什么我们经常看到"uncaught (in promise)"错误出现在微任务队列中。
3. 实现Promise.all
Promise.all是处理多个并行异步操作的利器,它接收一个Promise数组,返回一个新的Promise:
- 当所有Promise都成功时,新Promise以数组形式返回所有结果
- 当任一Promise失败时,立即拒绝
javascript复制MyPromise.all = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Arguments must be an array'));
}
const results = [];
let count = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
},
reason => {
reject(reason);
}
);
});
});
};
实际使用示例:
javascript复制const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));
const p3 = 3; // 非Promise值会被Promise.resolve包装
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [1, 2, 3]
});
常见陷阱:Promise.all在遇到第一个reject时会立即终止,不会等待其他Promise完成。如果需要等待所有Promise完成(无论成功失败),应该使用Promise.allSettled。
4. 实现Promise.race
Promise.race顾名思义就是"赛跑",哪个Promise先完成(无论成功失败)就采用它的结果:
javascript复制MyPromise.race = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Arguments must be an array'));
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
};
使用场景示例:
javascript复制// 设置超时控制
const fetchWithTimeout = (url, timeout = 5000) => {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
)
]);
};
fetchWithTimeout('https://api.example.com/data')
.then(response => response.json())
.catch(error => console.error(error));
实战技巧:Promise.race常用于实现请求超时、竞速请求等场景。但要注意,race不会取消其他未完成的Promise,它们仍会继续执行直到完成。
5. 实现Promise.allSettled
ES2020引入的Promise.allSettled解决了Promise.all"一票否决"的问题,它会等待所有Promise完成(无论成功失败),返回每个Promise的结果描述:
javascript复制MyPromise.allSettled = function(promises) {
return new MyPromise(resolve => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Arguments must be an array'));
}
const results = [];
let count = 0;
const checkComplete = () => {
if (count === promises.length) {
resolve(results);
}
};
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise)
.then(value => {
results[index] = { status: 'fulfilled', value };
})
.catch(reason => {
results[index] = { status: 'rejected', reason };
})
.finally(() => {
count++;
checkComplete();
});
});
});
};
使用示例:
javascript复制const promises = [
Promise.resolve(1),
Promise.reject('Error occurred'),
new Promise(resolve => setTimeout(resolve, 1000, 3))
];
Promise.allSettled(promises).then(results => {
console.log(results);
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'Error occurred' },
// { status: 'fulfilled', value: 3 }
// ]
});
业务场景:在需要收集所有异步操作结果的场景特别有用,比如批量操作后需要知道每个操作的成功失败情况,而不是遇到第一个错误就终止。
6. Promise常见问题与调试技巧
6.1 "Uncaught (in promise) Error"问题
这是Promise最常见的错误之一,通常是因为Promise链中没有添加catch处理:
javascript复制// 错误示例
function fetchData() {
return fetch('/api/data')
.then(response => response.json());
}
// 正确做法
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.catch(error => {
console.error('Fetch error:', error);
throw error; // 可以选择继续抛出或返回默认值
});
}
调试技巧:
- 始终为Promise链添加catch处理
- 在Node.js中可以使用
process.on('unhandledRejection', callback)捕获未处理的拒绝 - 在浏览器中监听
unhandledrejection事件:
javascript复制window.addEventListener('unhandledrejection', event => {
console.warn('Unhandled rejection:', event.reason);
event.preventDefault(); // 阻止默认错误输出
});
6.2 Promise内存泄漏
长时间pending的Promise可能导致内存泄漏,特别是在SPA应用中:
javascript复制// 潜在内存泄漏
let pendingPromise;
function fetchData() {
pendingPromise = fetch('/api/data');
return pendingPromise;
}
// 解决方案:提供取消机制
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
}
});
// 需要取消时
controller.abort();
6.3 Promise性能优化
- 避免不必要的Promise嵌套:
javascript复制// 反模式
function getUser(id) {
return new Promise(resolve => {
fetchUser(id).then(user => resolve(user));
});
}
// 直接返回即可
function getUser(id) {
return fetchUser(id);
}
- 合理使用Promise缓存:
javascript复制const userCache = new Map();
function getUserWithCache(id) {
if (userCache.has(id)) {
return Promise.resolve(userCache.get(id));
}
return fetchUser(id).then(user => {
userCache.set(id, user);
return user;
});
}
- 批量处理并发请求:
javascript复制// 反模式:逐个请求
async function fetchAllUsers(ids) {
const users = [];
for (const id of ids) {
users.push(await fetchUser(id));
}
return users;
}
// 优化:并发请求
function fetchAllUsers(ids) {
return Promise.all(ids.map(id => fetchUser(id)));
}
// 带并发控制的版本
async function fetchAllUsersWithLimit(ids, limit = 5) {
const batches = [];
for (let i = 0; i < ids.length; i += limit) {
batches.push(ids.slice(i, i + limit));
}
const results = [];
for (const batch of batches) {
const batchResults = await Promise.all(batch.map(id => fetchUser(id)));
results.push(...batchResults);
}
return results;
}
7. Promise进阶技巧与实战模式
7.1 实现Promise重试机制
javascript复制function retry(fn, retries = 3, delay = 1000) {
return new Promise((resolve, reject) => {
const attempt = (remaining) => {
fn()
.then(resolve)
.catch(err => {
if (remaining <= 0) {
reject(err);
} else {
console.log(`Retrying... (${remaining} attempts left)`);
setTimeout(() => attempt(remaining - 1), delay);
}
});
};
attempt(retries);
});
}
// 使用示例
retry(() => fetch('https://api.example.com/data'), 3)
.then(response => response.json())
.catch(console.error);
7.2 Promise与async/await的最佳实践
虽然async/await让异步代码更清晰,但仍需注意:
- 避免不必要的await:
javascript复制// 不必要
async function getData() {
const a = await getA();
const b = await getB(); // 顺序执行,可能影响性能
return { a, b };
}
// 优化:并行执行
async function getData() {
const [a, b] = await Promise.all([getA(), getB()]);
return { a, b };
}
- 正确处理循环中的异步操作:
javascript复制// 反模式:forEach中的async
async function processItems(items) {
items.forEach(async item => {
await processItem(item); // 不会等待
});
console.log('Done?'); // 可能提前执行
}
// 正确做法
async function processItems(items) {
for (const item of items) {
await processItem(item); // 顺序执行
}
// 或并行执行
await Promise.all(items.map(item => processItem(item)));
}
7.3 实现Promise超时控制
javascript复制Promise.withTimeout = function(promise, timeout, errorMsg = 'Timeout') {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => reject(new Error(errorMsg)), timeout);
});
return Promise.race([promise, timeoutPromise]).finally(() => {
clearTimeout(timeoutId);
});
};
// 使用示例
Promise.withTimeout(fetch('https://api.example.com/data'), 5000)
.then(response => response.json())
.catch(err => {
if (err.message === 'Timeout') {
console.log('Request timed out');
} else {
console.log('Other error:', err);
}
});
7.4 Promise取消模式
虽然原生Promise不支持取消,但可以通过包装实现:
javascript复制class CancelablePromise {
constructor(executor) {
this._promise = new Promise(executor);
this._isCanceled = false;
}
then(onFulfilled, onRejected) {
return this._promise.then(
value => this._isCanceled ? undefined : onFulfilled?.(value),
error => this._isCanceled ? undefined : onRejected?.(error)
);
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
cancel() {
this._isCanceled = true;
}
}
// 使用示例
const cp = new CancelablePromise(resolve => {
setTimeout(() => resolve('Done'), 2000);
});
cp.then(console.log); // 如果未取消,2秒后输出"Done"
// 1秒后取消
setTimeout(() => cp.cancel(), 1000);
8. Promise测试与调试
8.1 测试Promise的注意事项
- 确保测试框架支持异步测试:
javascript复制// Jest示例
test('fetchData returns expected data', async () => {
const data = await fetchData();
expect(data).toEqual(expectedData);
});
// 或者使用返回Promise的方式
test('fetchData returns expected data', () => {
return fetchData().then(data => {
expect(data).toEqual(expectedData);
});
});
- 测试Promise拒绝:
javascript复制test('fetchData rejects when network error', async () => {
await expect(fetchDataWithError()).rejects.toThrow('Network error');
});
8.2 使用Sinon模拟Promise
javascript复制import sinon from 'sinon';
test('should call API once', async () => {
const fetchStub = sinon.stub().resolves({ data: 'mock' });
const result = await fetchData(fetchStub);
expect(fetchStub.calledOnce).toBe(true);
expect(result).toEqual({ data: 'mock' });
});
8.3 调试Promise链的技巧
- 使用tap函数调试中间值:
javascript复制const tap = fn => x => (fn(x), x);
fetchData()
.then(tap(data => console.log('After fetch:', data)))
.then(processData)
.then(tap(processed => console.log('After process:', processed)))
.catch(console.error);
- 可视化Promise执行流程:
javascript复制function trace(label) {
return value => {
console.log(`${label}:`, value);
return value;
};
}
fetchData()
.then(trace('initial'))
.then(step1)
.then(trace('after step1'))
.then(step2)
.then(trace('after step2'))
.catch(trace('error'));
9. Promise在现代前端框架中的应用
9.1 React中的Promise模式
- 结合useEffect处理异步操作:
javascript复制function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetchUser(userId)
.then(data => {
setUser(data);
setError(null);
})
.catch(err => {
setError(err.message);
})
.finally(() => {
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user?.name}</div>;
}
- 使用自定义Hook封装Promise逻辑:
javascript复制function usePromise(promiseCreator, deps) {
const [state, setState] = useState({
data: null,
loading: false,
error: null
});
useEffect(() => {
let isMounted = true;
setState(prev => ({ ...prev, loading: true }));
promiseCreator()
.then(data => {
if (isMounted) {
setState({ data, loading: false, error: null });
}
})
.catch(error => {
if (isMounted) {
setState({ data: null, loading: false, error });
}
});
return () => {
isMounted = false;
};
}, deps);
return state;
}
// 使用示例
function UserProfile({ userId }) {
const { data: user, loading, error } = usePromise(
() => fetchUser(userId),
[userId]
);
// 渲染逻辑...
}
9.2 Vue中的Promise最佳实践
- 在组件中使用async/await:
javascript复制export default {
data() {
return {
user: null,
loading: false,
error: null
};
},
methods: {
async fetchUser() {
this.loading = true;
this.error = null;
try {
this.user = await fetchUser(this.userId);
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
},
created() {
this.fetchUser();
}
};
- 使用组合式API:
javascript复制import { ref, watchEffect } from 'vue';
export function useUser(userId) {
const user = ref(null);
const loading = ref(false);
const error = ref(null);
watchEffect(async () => {
loading.value = true;
error.value = null;
try {
user.value = await fetchUser(userId.value);
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
});
return { user, loading, error };
}
9.3 Angular中的Promise与Observable交互
虽然Angular主要使用RxJS Observable,但经常需要与Promise互操作:
typescript复制// 将Promise转换为Observable
import { from } from 'rxjs';
@Component({ /* ... */ })
export class UserComponent implements OnInit {
user$: Observable<User>;
ngOnInit() {
this.user$ = from(fetchUser(this.userId));
}
}
// 将Observable转换为Promise
async function getData() {
const data = await someObservable$.toPromise();
// 注意:toPromise()在RxJS 7中已弃用,可以使用lastValueFrom或firstValueFrom
const data = await lastValueFrom(someObservable$);
}
10. Promise性能优化与高级模式
10.1 实现Promise池(控制并发)
javascript复制class PromisePool {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.activeCount = 0;
}
add(task) {
return new Promise((resolve, reject) => {
const run = () => {
this.activeCount++;
task()
.then(resolve)
.catch(reject)
.finally(() => {
this.activeCount--;
this.next();
});
};
if (this.activeCount < this.maxConcurrent) {
run();
} else {
this.queue.push(run);
}
});
}
next() {
if (this.queue.length > 0 && this.activeCount < this.maxConcurrent) {
const task = this.queue.shift();
task();
}
}
}
// 使用示例
const pool = new PromisePool(3); // 最大并发3
const tasks = Array(10).fill(() => fetch('https://api.example.com/data'));
const results = await Promise.all(
tasks.map(task => pool.add(task))
);
10.2 实现Promise缓存
javascript复制function createPromiseCache(fn, keyFn = JSON.stringify) {
const cache = new Map();
return function(...args) {
const key = keyFn(args);
if (cache.has(key)) {
return cache.get(key);
}
const promise = fn(...args)
.then(result => {
// 可选:缓存成功后设置过期时间
return result;
})
.catch(err => {
cache.delete(key); // 失败时移除缓存
throw err;
});
cache.set(key, promise);
return promise;
};
}
// 使用示例
const cachedFetchUser = createPromiseCache(fetchUser);
// 多次调用只会发起一次请求
cachedFetchUser(1).then(/* ... */);
cachedFetchUser(1).then(/* ... */);
10.3 实现Promise重试与退避策略
javascript复制function retryWithBackoff(fn, options = {}) {
const {
retries = 3,
initialDelay = 1000,
maxDelay = 30000,
factor = 2,
jitter = true
} = options;
return new Promise((resolve, reject) => {
const attempt = (attemptNumber) => {
fn()
.then(resolve)
.catch(err => {
if (attemptNumber >= retries) {
return reject(err);
}
const delay = Math.min(
initialDelay * Math.pow(factor, attemptNumber),
maxDelay
);
const actualDelay = jitter
? delay * (0.5 + Math.random() * 0.5)
: delay;
console.log(`Attempt ${attemptNumber + 1} failed, retrying in ${Math.round(actualDelay)}ms`);
setTimeout(() => attempt(attemptNumber + 1), actualDelay);
});
};
attempt(0);
});
}
// 使用示例
retryWithBackoff(fetchData, {
retries: 5,
initialDelay: 1000,
factor: 2
}).then(processData).catch(console.error);
10.4 Promise与Web Worker的集成
javascript复制// worker.js
self.onmessage = function(e) {
const { id, task, args } = e.data;
try {
const result = executeTask(task, args); // 假设的task执行函数
self.postMessage({ id, result });
} catch (error) {
self.postMessage({ id, error: error.message });
}
};
// main.js
class PromiseWorker {
constructor(workerUrl) {
this.worker = new Worker(workerUrl);
this.promises = new Map();
this.idCounter = 0;
this.worker.onmessage = e => {
const { id, result, error } = e.data;
const { resolve, reject } = this.promises.get(id);
this.promises.delete(id);
if (error) {
reject(new Error(error));
} else {
resolve(result);
}
};
}
postMessage(task, args) {
const id = this.idCounter++;
return new Promise((resolve, reject) => {
this.promises.set(id, { resolve, reject });
this.worker.postMessage({ id, task, args });
});
}
}
// 使用示例
const worker = new PromiseWorker('worker.js');
worker.postMessage('heavyCalculation', { data: largeDataSet })
.then(result => console.log('Result:', result))
.catch(err => console.error('Error:', err));
11. Promise在Node.js中的特殊应用
11.1 使用util.promisify转换回调API
javascript复制const fs = require('fs');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
async function processFile(filePath) {
try {
const content = await readFile(filePath, 'utf8');
console.log(content);
} catch (err) {
console.error('Error reading file:', err);
}
}
11.2 实现自定义promisify
javascript复制function myPromisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
}
// 使用示例
const customReadFile = myPromisify(fs.readFile);
customReadFile('file.txt', 'utf8').then(console.log).catch(console.error);
11.3 Promise与Stream的集成
javascript复制const { pipeline } = require('stream');
const { promisify } = require('util');
const pipelineAsync = promisify(pipeline);
async function processLargeFile(inputPath, outputPath) {
try {
await pipelineAsync(
fs.createReadStream(inputPath),
transformStream, // 假设的转换流
fs.createWriteStream(outputPath)
);
console.log('Processing completed');
} catch (err) {
console.error('Pipeline failed:', err);
}
}
11.4 使用Promise实现中间件流程控制
javascript复制function compose(middlewares) {
return function(context, next) {
let index = -1;
function dispatch(i) {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'));
}
index = i;
let fn = middlewares[i];
if (i === middlewares.length) {
fn = next;
}
if (!fn) {
return Promise.resolve();
}
try {
return Promise.resolve(
fn(context, () => dispatch(i + 1))
);
} catch (err) {
return Promise.reject(err);
}
}
return dispatch(0);
};
}
// 使用示例
const middlewares = [
async (ctx, next) => {
console.log('Middleware 1 start');
await next();
console.log('Middleware 1 end');
},
async (ctx, next) => {
console.log('Middleware 2 start');
await next();
console.log('Middleware 2 end');
}
];
const run = compose(middlewares);
run({}).then(() => console.log('All middlewares completed'));
12. Promise的未来与替代方案
12.1 Promise与Observable的比较
| 特性 | Promise | Observable |
|---|---|---|
| 执行时机 | 创建即执行 | 订阅才执行 |
| 数据流 | 单值 | 多值(流) |
| 可取消性 | 原生不支持 | 支持取消 |
| 组合能力 | 有限 | 强大的操作符 |
| 适用场景 | 一次性异步操作 | 持续事件流 |
12.2 使用AbortController取消Promise
javascript复制function cancellableFetch(url, { signal } = {}) {
if (signal && signal.aborted) {
return Promise.reject(new DOMException('Aborted', 'AbortError'));
}
return new Promise((resolve, reject) => {
const controller = new AbortController();
const abortHandler = () => {
controller.abort();
cleanup();
};
const cleanup = () => {
if (signal) {
signal.removeEventListener('abort', abortHandler);
}
};
if (signal) {
signal.addEventListener('abort', abortHandler);
}
fetch(url, { signal: controller.signal })
.then(response => {
cleanup();
resolve(response);
})
.catch(err => {
cleanup();
reject(err);
});
});
}
// 使用示例
const controller = new AbortController();
cancellableFetch('https://api.example.com/data', { signal: controller.signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Request was aborted');
} else {
console.error('Fetch error:', err);
}
});
// 取消请求
controller.abort();
12.3 使用async_hooks跟踪Promise链
Node.js的async_hooks模块可以跟踪异步资源(包括Promise)的生命周期:
javascript复制const async_hooks = require('async_hooks');
const fs = require('fs');
// 跟踪Promise执行
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
if (type === 'PROMISE') {
fs.writeSync(1, `Promise created: ${asyncId}\n`);
}
},
before(asyncId) {
fs.writeSync(1, `Promise before: ${asyncId}\n`);
},
after(asyncId) {
fs.writeSync(1, `Promise after: ${asyncId}\n`);
},
destroy(asyncId) {
fs.writeSync(1, `Promise destroyed: ${asyncId}\n`);
}
});
hook.enable();
// 示例Promise链
Promise.resolve()
.then(() => {
return Promise.all([delay(100), delay(200)]);
})
.then(() => {
return delay(300);
});
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
12.4 探索中的TC39提案:Promise.try
提案中的Promise.try允许同步和异步错误都能被捕获:
javascript复制// 当前方式
function getUser(id) {
return Promise.resolve().then(() => {
if (!id) throw new Error('ID required');
return fetchUser(id);
});
}
// 使用Promise.try提案
function getUser(id) {
return Promise.try(() => {
if (!id) throw new Error('ID required');
return fetchUser(id);
});
}
虽然这个提案尚未正式通过,但我们可以自己实现:
javascript复制Promise.try = function(fn) {
try {
return Promise.resolve(fn());
} catch (err) {
return Promise.reject(err);
}
};
