```html
JavaScript异步编程:从回调地狱到Promise的艺术
1. 回调函数的时代
在早期JavaScript中,异步操作主要依赖回调函数。虽然简单直接,但嵌套过深时就会出现所谓的回调地狱。
回调地狱示例:
// 典型的回调地狱
readFile('file1.txt', function(err, data1) {
if (err) throw err;
processData(data1, function(err, result1) {
if (err) throw err;
readFile('file2.txt', function(err, data2) {
if (err) throw err;
processData(data2, function(err, result2) {
if (err) throw err;
// 更多嵌套...
});
});
});
});
问题:
- 代码难以阅读和维护
- 错误处理复杂
- 难以进行流程控制
- 代码缩进过深
2. Promise的诞生
Promise的出现为异步编程带来了革命性的改变,它代表一个异步操作的最终完成(或失败)及其结果值。
Promise基础:
// Promise基本用法
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
readFile(filename, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
// 链式调用
readFilePromise('file1.txt')
.then(data => processData(data))
.then(result => readFilePromise('file2.txt'))
.then(data => processData(data))
.catch(err => console.error('Error:', err));
Promise的优势:
- 链式调用,避免深层嵌套
- 统一的错误处理
- 更好的流程控制
- 可组合性
3. Promise的艺术与技巧
// 1. Promise组合
const promise1 = readFilePromise('file1.txt');
const promise2 = readFilePromise('file2.txt');
// 并行执行
Promise.all([promise1, promise2])
.then(([data1, data2]) => {
console.log('All files loaded:', data1, data2);
})
.catch(err => console.error('One of the promises failed:', err));
// 2. Promise.race - 竞速
Promise.race([promise1, promise2])
.then(firstResult => {
console.log('First file loaded:', firstResult);
});
// 3. 错误处理的艺术
function robustApiCall() {
return apiCall()
.then(data => ({ success: true, data }))
.catch(error => ({ success: false, error }));
}
// 4. Promise链中的值传递
readFilePromise('config.json')
.then(config => {
return {
config: JSON.parse(config),
timestamp: Date.now()
};
})
.then(({ config, timestamp }) => {
return initializeApp(config, timestamp);
});
4. 高级Promise模式
// 1. 重试机制
function retryOperation(operation, retries = 3, delay = 1000) {
return new Promise((resolve, reject) => {
const attempt = (n) => {
operation()
.then(resolve)
.catch(error => {
if (n === 0) {
reject(error);
} else {
console.log(`Retry ${retries - n + 1}/${retries}`);
setTimeout(() => attempt(n - 1), delay);
}
});
};
attempt(retries);
});
}
// 2. 超时控制
function withTimeout(promise, timeoutMs) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Operation timeout')), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// 3. 顺序执行Promise数组
function sequentialPromises(promises) {
return promises.reduce((chain, promise) => {
return chain.then(() => promise());
}, Promise.resolve());
}
// 4. Promise缓存
function createCachedPromise(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const promise = fn(...args).finally(() => {
cache.delete(key);
});
cache.set(key, promise);
return promise;
};
}
5. 从回调到Promise的优雅转换
// 使用util.promisify(Node.js)
const util = require('util');
const readFileAsync = util.promisify(readFile);
// 手动包装回调函数
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
}
// 将整个回调风格的API转换为Promise风格
class PromiseBasedAPI {
constructor(callbackBasedAPI) {
this.api = callbackBasedAPI;
}
method1(param) {
return new Promise((resolve, reject) => {
this.api.method1(param, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
method2(param1, param2) {
return new Promise((resolve, reject) => {
this.api.method2(param1, param2, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
}
6. 最佳实践与注意事项
// 1. 总是返回Promise
function goodPractice() {
return doAsyncWork()
.then(result => {
// 处理结果
return processedResult;
});
// 注意:不要忘记return!
}
// 2. 正确处理错误
function properErrorHandling() {
return apiCall()
.then(data => {
if (!data.isValid) {
throw new Error('Invalid data'); // 同步错误也会被catch捕获
}
return data;
})
.catch(error => {
console.error('API call failed:', error);
throw error; // 重新抛出以保持错误传播
});
}
// 3. 避免Promise构造函数反模式
// 错误做法:
function antiPattern() {
return new Promise((resolve, reject) => {
getData().then(data => {
processData(data).then(result => {
resolve(result); // 不必要的嵌套
});
});
});
}
// 正确做法:
function correctPattern() {
return getData()
.then(data => processData(data));
}
```
3386

被折叠的 条评论
为什么被折叠?



