异步处理规范
异步处理是JavaScript编程中的重要概念,合理的规范能提升代码可读性和可维护性。以下是常见的异步处理规范和实践方式。
Promise使用规范
Promise是处理异步操作的基础工具,使用时需遵循以下规则:
- 链式调用:避免嵌套Promise,使用
.then()
链式调用
// 正确示例
fetchData()
.then(processData)
.then(saveData)
.catch(handleError);
// 错误示例
fetchData().then(data => {
processData(data).then(result => {
saveData(result).catch(err => {
handleError(err);
});
});
});
- 错误处理:每个Promise链末尾必须包含
.catch()
getUserInfo()
.then(updateUI)
.catch(logError); // 必须捕获错误
- 返回完整Promise:在
.then()
内部返回完整Promise对象
// 正确
fetch('/api').then(response => {
return response.json();
});
// 错误
fetch('/api').then(response => {
response.json(); // 缺少return
});
async/await最佳实践
ES2017引入的async/await使异步代码更接近同步写法:
- 统一错误处理:使用try-catch包裹await调用
async function loadData() {
try {
const data = await fetchData();
const processed = await processData(data);
return processed;
} catch (error) {
console.error('加载失败:', error);
throw error; // 重新抛出供上层处理
}
}
- 避免冗余await:非依赖关系使用并行执行
// 低效写法
const user = await getUser();
const posts = await getPosts();
// 优化写法
const [user, posts] = await Promise.all([
getUser(),
getPosts()
]);
- 明确async标记:异步函数必须显式声明async
// 正确
async function fetchResource() { /*...*/ }
// 错误
function fetchResource() { // 缺少async
return await someAsyncOp();
}
回调函数规范
在必须使用回调函数的场景(如Node.js传统API):
- 错误优先原则:回调参数第一个位置必须是error
fs.readFile('config.json', (err, data) => {
if (err) return handleError(err);
processConfig(data);
});
- 避免多层嵌套:超过3层应重构为Promise
// 需要重构的代码
getUser(userId, (err, user) => {
if (err) return console.error(err);
getPermissions(user, (err, permissions) => {
if (err) return console.error(err);
checkAccess(permissions, (err, access) => {
if (err) return console.error(err);
// 业务逻辑...
});
});
});
事件发射器规范
处理EventEmitter类型异步事件时:
- 事件监听清理:必须移除不再需要的事件监听
const emitter = new EventEmitter();
function listener(data) {
console.log('Received:', data);
emitter.off('data', listener); // 使用后立即清理
}
emitter.on('data', listener);
- 错误事件处理:必须监听error事件
const stream = createReadStream('file.txt');
stream.on('error', (err) => {
console.error('流错误:', err);
});
微任务与宏任务
理解执行顺序对复杂异步逻辑至关重要:
console.log('脚本开始');
setTimeout(() => {
console.log('宏任务');
}, 0);
Promise.resolve().then(() => {
console.log('微任务');
});
console.log('脚本结束');
// 输出顺序:
// 脚本开始
// 脚本结束
// 微任务
// 宏任务
取消异步操作
实现可取消的异步流程:
- AbortController方案:
const controller = new AbortController();
fetch('/api', {
signal: controller.signal
}).catch(err => {
if (err.name === 'AbortError') {
console.log('请求已取消');
}
});
// 取消请求
controller.abort();
- 自定义取消令牌:
function createCancellablePromise(executor) {
let rejectFn;
const promise = new Promise((resolve, reject) => {
rejectFn = reject;
executor(resolve, reject);
});
return {
promise,
cancel: (reason) => {
rejectFn(new Error(reason || 'Cancelled'));
}
};
}
异步状态管理
在状态管理中处理异步操作:
// Redux异步action示例
const fetchUser = (userId) => async (dispatch) => {
dispatch({ type: 'USER_FETCH_START' });
try {
const response = await api.getUser(userId);
dispatch({ type: 'USER_FETCH_SUCCESS', payload: response });
} catch (error) {
dispatch({ type: 'USER_FETCH_ERROR', error });
}
};
// Vuex示例
actions: {
async loadPosts({ commit }) {
commit('SET_LOADING', true);
const posts = await fetchPosts();
commit('SET_POSTS', posts);
commit('SET_LOADING', false);
}
}
性能优化策略
提升异步操作性能的实用技巧:
- 请求去重:避免重复发起相同请求
const pendingRequests = new Map();
async function cachedFetch(url) {
if (pendingRequests.has(url)) {
return pendingRequests.get(url);
}
const promise = fetch(url).then(res => res.json());
pendingRequests.set(url, promise);
try {
return await promise;
} finally {
pendingRequests.delete(url);
}
}
- 批量处理:合并多个异步操作
async function batchUpdateItems(items) {
const BATCH_SIZE = 10;
const batches = [];
for (let i = 0; i < items.length; i += BATCH_SIZE) {
batches.push(items.slice(i, i + BATCH_SIZE));
}
const results = [];
for (const batch of batches) {
const batchResults = await Promise.all(
batch.map(item => updateItem(item))
);
results.push(...batchResults);
}
return results;
}
测试异步代码
编写可靠的异步测试用例:
// Jest测试示例
describe('异步函数测试', () => {
// 回调方式
test('回调执行', done => {
fetchData((err, data) => {
expect(err).toBeNull();
expect(data).toHaveProperty('id');
done();
});
});
// Promise方式
test('Promise解析', () => {
return fetchData().then(data => {
expect(data.status).toBe(200);
});
});
// async/await方式
test('async/await测试', async () => {
const data = await fetchData();
expect(data.items).toBeInstanceOf(Array);
});
// 定时器模拟
jest.useFakeTimers();
test('定时器测试', () => {
const callback = jest.fn();
delayedCallback(callback);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
});
});
浏览器与Node差异
不同环境下异步处理的区别:
- 全局对象差异:
// 浏览器环境
window.setTimeout(() => {
// 浏览器特有API
}, 100);
// Node环境
global.setImmediate(() => {
// Node特有API
});
- 模块加载差异:
// Node CommonJS
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
// 回调处理
});
// 浏览器ES模块
import('./module.js').then(module => {
module.doSomething();
});
错误追踪与调试
增强异步错误的可调试性:
- 添加上下文信息:
async function wrappedFetch(url) {
try {
return await fetch(url);
} catch (err) {
err.context = { url, timestamp: Date.now() };
throw err;
}
}
- 长期任务监控:
function withTimeout(promise, timeout, taskName) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`${taskName} 超时 (${timeout}ms)`));
}, timeout);
})
]);
}
并发控制
管理并行异步任务的数量:
class TaskQueue {
constructor(concurrency) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
push(task) {
this.queue.push(task);
this.next();
}
next() {
while (this.running < this.concurrency && this.queue.length) {
const task = this.queue.shift();
task().finally(() => {
this.running--;
this.next();
});
this.running++;
}
}
}
// 使用示例
const queue = new TaskQueue(3);
urls.forEach(url => {
queue.push(() => fetchUrl(url));
});
异步模式演进
从历史角度看待异步处理发展:
- 回调地狱时期:
// 2013年典型代码
readFile('a.txt', (err, a) => {
if (err) return handle(err);
readFile('b.txt', (err, b) => {
if (err) return handle(err);
writeFile('c.txt', a + b, (err) => {
if (err) return handle(err);
console.log('完成!');
});
});
});
- Promise过渡期:
// 2015年典型改进
readFile('a.txt')
.then(a => readFile('b.txt').then(b => [a, b]))
.then(([a, b]) => writeFile('c.txt', a + b))
.then(() => console.log('完成'))
.catch(handle);
- 现代async/await:
// 2020+现代写法
async function concatFiles() {
try {
const a = await readFile('a.txt');
const b = await readFile('b.txt');
await writeFile('c.txt', a + b);
console.log('完成');
} catch (err) {
handle(err);
}
}
实用工具函数
收集常用的异步辅助函数:
- 重试机制:
async function retry(fn, retries = 3, delay = 1000) {
try {
return await fn();
} catch (err) {
if (retries <= 0) throw err;
await new Promise(res => setTimeout(res, delay));
return retry(fn, retries - 1, delay * 2);
}
}
- 进度通知:
async function withProgress(promise, onProgress) {
let progress = 0;
const interval = setInterval(() => {
progress = Math.min(progress + 10, 90);
onProgress(progress);
}, 100);
try {
const result = await promise;
clearInterval(interval);
onProgress(100);
return result;
} catch (err) {
clearInterval(interval);
throw err;
}
}
浏览器API集成
与浏览器API配合的异步模式:
- Web Worker通信:
// 主线程
const worker = new Worker('task.js');
worker.postMessage({ cmd: 'start', data: 42 });
worker.onmessage = (e) => {
console.log('结果:', e.data);
};
// worker.js
self.onmessage = async (e) => {
if (e.data.cmd === 'start') {
const result = await heavyComputation(e.data.data);
self.postMessage(result);
}
};
- IndexedDB事务:
async function saveRecord(dbName, record) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName);
request.onsuccess = (e) => {
const db = e.target.result;
const tx = db.transaction('store', 'readwrite');
const store = tx.objectStore('store');
const putRequest = store.put(record);
putRequest.onsuccess = () => resolve();
putRequest.onerror = (e) => reject(e.target.error);
};
request.onerror = (e) => reject(e.target.error);
});
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn