Node.js Promise 对象
核心知识点
Promise 概念
Promise 是一种用于处理异步操作的对象,它代表一个可能在未来完成或失败的操作。在 Node.js 中,Promise 提供了一种更优雅的方式来处理异步代码,避免回调地狱。
Promise 状态
Promise 有三种状态:
- Pending(待定):初始状态,操作尚未完成
- Fulfilled(已完成):操作成功完成
- Rejected(已拒绝):操作失败
Promise 基础语法
创建和使用 Promise 的基本语法:
const promise = new Promise((resolve, reject) => {
// 执行异步操作
if (操作成功) {
resolve(result); // 成功时调用 resolve
} else {
reject(error); // 失败时调用 reject
}
});
// 使用 Promise
promise
.then(result => {
// 处理成功结果
})
.catch(error => {
// 处理错误
});Promise 链式调用
Promise 可以通过链式调用处理多个异步操作:
promise
.then(result1 => {
// 处理第一个结果
return processResult(result1); // 返回另一个 Promise
})
.then(result2 => {
// 处理第二个结果
return anotherOperation(result2); // 返回另一个 Promise
})
.then(result3 => {
// 处理最终结果
})
.catch(error => {
// 处理任何环节的错误
});Promise 静态方法
Promise 提供了几个有用的静态方法:
- **Promise.all()**:等待所有 Promise 完成
- **Promise.race()**:等待第一个完成的 Promise
- **Promise.resolve()**:创建一个已完成的 Promise
- **Promise.reject()**:创建一个已拒绝的 Promise
实用案例
案例一:基本 Promise 使用
const fs = require('fs');
// 封装文件读取为 Promise
function readFilePromise(filename, encoding) {
return new Promise((resolve, reject) => {
fs.readFile(filename, encoding, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
// 封装文件写入为 Promise
function writeFilePromise(filename, content) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, content, (err) => {
if (err) {
reject(err);
} else {
resolve('File written successfully');
}
});
});
}
// 使用 Promise 处理文件操作
readFilePromise('example.txt', 'utf8')
.then(data => {
console.log('读取的文件内容:', data);
return writeFilePromise('output.txt', data.toUpperCase());
})
.then(message => {
console.log(message);
return readFilePromise('output.txt', 'utf8');
})
.then(newData => {
console.log('写入后读取的内容:', newData);
})
.catch(err => {
console.error('发生错误:', err);
});案例二:Promise 链式调用
const http = require('http');
// 封装 HTTP 请求为 Promise
function httpGetPromise(url) {
return new Promise((resolve, reject) => {
http.get(url, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
resolve(data);
});
response.on('error', (error) => {
reject(error);
});
}).on('error', (error) => {
reject(error);
});
});
}
// 链式调用 Promise
httpGetPromise('http://example.com')
.then(data => {
console.log('获取到数据,长度:', data.length);
// 处理数据
const processedData = data.substring(0, 1000);
console.log('处理后的数据长度:', processedData.length);
return processedData;
})
.then(processedData => {
// 进一步处理
console.log('数据前50个字符:', processedData.substring(0, 50) + '...');
return '处理完成';
})
.then(result => {
console.log('最终结果:', result);
})
.catch(err => {
console.error('发生错误:', err);
});案例三:Promise 静态方法
const fs = require('fs');
// 封装文件读取为 Promise
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
// 使用 Promise.all() 并行读取多个文件
Promise.all([
readFilePromise('file1.txt'),
readFilePromise('file2.txt'),
readFilePromise('file3.txt')
])
.then(results => {
console.log('所有文件读取完成');
console.log('文件1内容:', results[0]);
console.log('文件2内容:', results[1]);
console.log('文件3内容:', results[2]);
// 合并内容
const combinedContent = results.join('\n');
console.log('合并后的内容:', combinedContent);
})
.catch(err => {
console.error('读取文件时发生错误:', err);
});
// 使用 Promise.race() 竞争读取
Promise.race([
readFilePromise('file1.txt'),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('超时')), 1000);
})
])
.then(data => {
console.log('最快完成的操作结果:', data);
})
.catch(err => {
console.error('竞争失败:', err);
});案例四:使用 Promise 重构回调代码
const fs = require('fs');
// 回调版本
function processFilesCallback() {
fs.readFile('file1.txt', 'utf8', (err1, data1) => {
if (err1) {
console.error('读取文件1失败:', err1);
return;
}
fs.readFile('file2.txt', 'utf8', (err2, data2) => {
if (err2) {
console.error('读取文件2失败:', err2);
return;
}
const combined = data1 + data2;
fs.writeFile('combined.txt', combined, (err3) => {
if (err3) {
console.error('写入文件失败:', err3);
return;
}
console.log('文件处理完成');
});
});
});
}
// Promise 版本
function readFile(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
function writeFile(filename, content) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, content, (err) => {
if (err) reject(err);
else resolve('写入成功');
});
});
}
function processFilesPromise() {
readFile('file1.txt')
.then(data1 => {
return Promise.all([Promise.resolve(data1), readFile('file2.txt')]);
})
.then(([data1, data2]) => {
const combined = data1 + data2;
return writeFile('combined.txt', combined);
})
.then(message => {
console.log(message);
console.log('文件处理完成');
})
.catch(err => {
console.error('处理文件时发生错误:', err);
});
}
// 调用两种版本
console.log('使用回调处理文件:');
processFilesCallback();
console.log('\n使用 Promise 处理文件:');
processFilesPromise();学习目标
- 理解 Promise 概念:掌握 Promise 的基本原理和状态变化
- 掌握 Promise 语法:学会创建和使用 Promise
- 熟练链式调用:能够使用 Promise 链式处理多个异步操作
- 掌握错误处理:学会在 Promise 中处理错误
- 使用静态方法:掌握 Promise.all() 等静态方法的使用
- 重构回调代码:能够将回调风格的代码重构为 Promise 风格
代码优化建议
1. 合理使用 Promise 链式调用
不好的做法:
// 嵌套的 Promise
promise1.then(result1 => {
promise2.then(result2 => {
promise3.then(result3 => {
console.log(result1, result2, result3);
});
});
});好的做法:
// 链式调用
promise1
.then(result1 => {
return promise2;
})
.then(result2 => {
return promise3;
})
.then(result3 => {
console.log(result3);
});2. 正确处理错误
不好的做法:
promise
.then(result => {
// 处理结果
if (someErrorCondition) {
throw new Error('发生错误');
}
return nextOperation(result);
})
// 缺少 catch好的做法:
promise
.then(result => {
// 处理结果
if (someErrorCondition) {
throw new Error('发生错误');
}
return nextOperation(result);
})
.catch(error => {
console.error('处理错误:', error);
// 可以在这里进行错误恢复
});3. 使用 Promise.all() 并行处理
不好的做法:
// 串行处理,效率低
asyncOperation1()
.then(result1 => {
return asyncOperation2();
})
.then(result2 => {
return asyncOperation3();
})
.then(result3 => {
console.log(result1, result2, result3);
});好的做法:
// 并行处理,效率高
Promise.all([
asyncOperation1(),
asyncOperation2(),
asyncOperation3()
])
.then(([result1, result2, result3]) => {
console.log(result1, result2, result3);
});常见问题与解决方案
问题1:Promise 未处理的拒绝
原因:
- Promise 被拒绝但没有对应的 catch 处理
解决方案:
- 始终为 Promise 添加 catch 处理
- 可以在全局添加 unhandledRejection 事件监听器
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason);
});问题2:Promise 链式调用中的错误传播
原因:
- 链式调用中的某个 Promise 被拒绝
解决方案:
- 在链式调用的末尾添加 catch 处理
- 可以在中间添加 catch 处理特定错误并继续链式调用
问题3:Promise 内存泄漏
原因:
- 创建了 Promise 但没有正确处理(既没有 then 也没有 catch)
解决方案:
- 确保每个 Promise 都有对应的处理
- 使用 Promise.race() 设置超时
总结
Promise 是 Node.js 中处理异步操作的重要工具,通过本教程的学习,你应该能够:
- 理解 Promise 的基本概念和状态变化
- 掌握 Promise 的创建和使用方法
- 熟练使用 Promise 链式调用处理多个异步操作
- 正确处理 Promise 中的错误
- 掌握 Promise 静态方法的使用
- 将回调风格的代码重构为 Promise 风格
Promise 为异步编程提供了更清晰、更可维护的方式,是学习更高级的 async/await 语法的基础。在后续章节中,我们将学习 async/await,它是基于 Promise 的更现代的异步编程解决方案。