您现在的位置是:网站首页 > Promise原理与使用文章详情
Promise原理与使用
陈川
【
Node.js
】
60643人已围观
7969字
Promise的基本概念
Promise是JavaScript中处理异步操作的一种机制,它代表一个尚未完成但预期将来会完成的操作。Promise对象有三种状态:
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
resolve(randomNumber);
} else {
reject(new Error('数字太小'));
}
}, 1000);
});
Promise的核心原理
Promise的实现基于观察者模式,内部维护了两个队列:
- 成功回调队列(onFulfilledCallbacks)
- 失败回调队列(onRejectedCallbacks)
当Promise状态改变时,会依次执行对应队列中的回调函数。这种设计使得异步操作可以按照顺序执行,避免了回调地狱。
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb(value));
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
this.onRejectedCallbacks.forEach(cb => cb(reason));
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
Promise的常用方法
then方法
then方法是Promise的核心,它接收两个参数:
- onFulfilled:成功回调
- onRejected:失败回调
promise.then(
value => {
console.log('成功:', value);
},
error => {
console.error('失败:', error);
}
);
catch方法
catch方法是then(null, onRejected)的语法糖,专门用于处理错误。
promise
.then(value => {
console.log('成功:', value);
})
.catch(error => {
console.error('捕获错误:', error);
});
finally方法
finally方法无论Promise最终状态如何都会执行,适合用于清理工作。
promise
.then(value => {
console.log('成功:', value);
})
.catch(error => {
console.error('错误:', error);
})
.finally(() => {
console.log('操作完成');
});
Promise的静态方法
Promise.resolve
创建一个立即resolve的Promise。
const resolvedPromise = Promise.resolve('立即解决');
resolvedPromise.then(value => console.log(value)); // 输出: 立即解决
Promise.reject
创建一个立即reject的Promise。
const rejectedPromise = Promise.reject(new Error('立即拒绝'));
rejectedPromise.catch(error => console.error(error)); // 输出: Error: 立即拒绝
Promise.all
等待所有Promise完成,或者第一个Promise失败。
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1000));
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log(values); // 输出: [1, 2, 3]
})
.catch(error => {
console.error(error);
});
Promise.race
返回第一个settled的Promise(无论成功或失败)。
const promise1 = new Promise(resolve => setTimeout(() => resolve(1), 500));
const promise2 = new Promise(resolve => setTimeout(() => resolve(2), 200));
Promise.race([promise1, promise2])
.then(value => {
console.log(value); // 输出: 2
});
Promise的高级用法
Promise链式调用
Promise的then方法返回一个新的Promise,使得链式调用成为可能。
function fetchUser(userId) {
return Promise.resolve({ id: userId, name: '张三' });
}
function fetchPosts(userId) {
return Promise.resolve([
{ id: 1, title: '文章1' },
{ id: 2, title: '文章2' }
]);
}
fetchUser(123)
.then(user => {
console.log('用户:', user);
return fetchPosts(user.id);
})
.then(posts => {
console.log('文章:', posts);
})
.catch(error => {
console.error('错误:', error);
});
Promise错误处理
Promise的错误会沿着链一直传递,直到被catch捕获。
Promise.resolve()
.then(() => {
throw new Error('第一个错误');
})
.then(() => {
console.log('这里不会执行');
})
.catch(error => {
console.error('捕获错误:', error); // 输出: Error: 第一个错误
throw new Error('第二个错误');
})
.catch(error => {
console.error('捕获第二个错误:', error); // 输出: Error: 第二个错误
});
Promise与async/await
async/await是建立在Promise之上的语法糖,使异步代码看起来像同步代码。
async function getUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
return { user, posts };
} catch (error) {
console.error('获取数据失败:', error);
throw error;
}
}
getUserData(123)
.then(data => console.log('数据:', data))
.catch(error => console.error('最终错误:', error));
Promise的实际应用场景
文件读取
const fs = require('fs').promises;
async function readFiles() {
try {
const file1 = await fs.readFile('file1.txt', 'utf8');
const file2 = await fs.readFile('file2.txt', 'utf8');
console.log(file1, file2);
} catch (error) {
console.error('读取文件失败:', error);
}
}
readFiles();
网络请求
function fetchData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`请求失败: ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('网络错误'));
xhr.send();
});
}
fetchData('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error(error));
定时器封装
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function timedOperation() {
console.log('开始');
await delay(1000);
console.log('1秒后');
await delay(2000);
console.log('再过2秒后');
}
timedOperation();
Promise的注意事项
避免Promise嵌套
虽然Promise解决了回调地狱,但不正确的使用仍会导致Promise嵌套。
// 不推荐
getUser(userId).then(user => {
getPosts(user.id).then(posts => {
getComments(posts[0].id).then(comments => {
console.log(comments);
});
});
});
// 推荐
getUser(userId)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(error => console.error(error));
不要忘记返回Promise
在then回调中,如果忘记返回Promise,链式调用会中断。
// 错误示例
getUser(userId)
.then(user => {
getPosts(user.id); // 忘记return
})
.then(posts => {
console.log(posts); // undefined
});
// 正确示例
getUser(userId)
.then(user => {
return getPosts(user.id);
})
.then(posts => {
console.log(posts);
});
处理未捕获的Promise错误
未捕获的Promise错误可能会导致难以调试的问题。
// 全局捕获未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的拒绝:', reason);
});
// 或者在浏览器中
window.addEventListener('unhandledrejection', event => {
console.error('未处理的拒绝:', event.reason);
event.preventDefault();
});
Promise的性能优化
并行执行
使用Promise.all可以并行执行多个独立操作,提高效率。
async function fetchAllData() {
const [user, posts, comments] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
fetchComments(userId)
]);
return { user, posts, comments };
}
限制并发数
当需要处理大量Promise时,控制并发数可以避免资源耗尽。
async function runWithConcurrency(tasks, concurrency) {
const results = [];
const executing = [];
for (const task of tasks) {
const p = task().then(result => {
executing.splice(executing.indexOf(p), 1);
return result;
});
executing.push(p);
results.push(p);
if (executing.length >= concurrency) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
取消Promise
原生Promise不支持取消,但可以通过包装实现。
function cancellablePromise(executor) {
let rejectFn;
const promise = new Promise((resolve, reject) => {
rejectFn = reject;
executor(resolve, reject);
});
promise.cancel = (reason) => {
rejectFn(new Error(reason || 'Promise被取消'));
};
return promise;
}
const p = cancellablePromise((resolve, reject) => {
setTimeout(() => resolve('完成'), 2000);
});
p.then(console.log).catch(console.error);
// 1秒后取消
setTimeout(() => p.cancel('用户取消'), 1000);
上一篇: 回调函数模式
下一篇: async/await语法糖