阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 异步处理规范

异步处理规范

作者:陈川 阅读数:23529人阅读 分类: JavaScript

异步处理是JavaScript编程中的重要概念,合理的规范能提升代码可读性和可维护性。以下是常见的异步处理规范和实践方式。

Promise使用规范

Promise是处理异步操作的基础工具,使用时需遵循以下规则:

  1. 链式调用:避免嵌套Promise,使用.then()链式调用
// 正确示例
fetchData()
  .then(processData)
  .then(saveData)
  .catch(handleError);

// 错误示例
fetchData().then(data => {
  processData(data).then(result => {
    saveData(result).catch(err => {
      handleError(err);
    });
  });
});
  1. 错误处理:每个Promise链末尾必须包含.catch()
getUserInfo()
  .then(updateUI)
  .catch(logError);  // 必须捕获错误
  1. 返回完整Promise:在.then()内部返回完整Promise对象
// 正确
fetch('/api').then(response => {
  return response.json();
});

// 错误
fetch('/api').then(response => {
  response.json();  // 缺少return
});

async/await最佳实践

ES2017引入的async/await使异步代码更接近同步写法:

  1. 统一错误处理:使用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;  // 重新抛出供上层处理
  }
}
  1. 避免冗余await:非依赖关系使用并行执行
// 低效写法
const user = await getUser();
const posts = await getPosts();

// 优化写法
const [user, posts] = await Promise.all([
  getUser(),
  getPosts()
]);
  1. 明确async标记:异步函数必须显式声明async
// 正确
async function fetchResource() { /*...*/ }

// 错误
function fetchResource() {  // 缺少async
  return await someAsyncOp();
}

回调函数规范

在必须使用回调函数的场景(如Node.js传统API):

  1. 错误优先原则:回调参数第一个位置必须是error
fs.readFile('config.json', (err, data) => {
  if (err) return handleError(err);
  processConfig(data);
});
  1. 避免多层嵌套:超过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类型异步事件时:

  1. 事件监听清理:必须移除不再需要的事件监听
const emitter = new EventEmitter();

function listener(data) {
  console.log('Received:', data);
  emitter.off('data', listener);  // 使用后立即清理
}

emitter.on('data', listener);
  1. 错误事件处理:必须监听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('脚本结束');

// 输出顺序:
// 脚本开始
// 脚本结束
// 微任务
// 宏任务

取消异步操作

实现可取消的异步流程:

  1. AbortController方案
const controller = new AbortController();

fetch('/api', {
  signal: controller.signal
}).catch(err => {
  if (err.name === 'AbortError') {
    console.log('请求已取消');
  }
});

// 取消请求
controller.abort();
  1. 自定义取消令牌
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);
  }
}

性能优化策略

提升异步操作性能的实用技巧:

  1. 请求去重:避免重复发起相同请求
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);
  }
}
  1. 批量处理:合并多个异步操作
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差异

不同环境下异步处理的区别:

  1. 全局对象差异
// 浏览器环境
window.setTimeout(() => {
  // 浏览器特有API
}, 100);

// Node环境
global.setImmediate(() => {
  // Node特有API
});
  1. 模块加载差异
// Node CommonJS
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
  // 回调处理
});

// 浏览器ES模块
import('./module.js').then(module => {
  module.doSomething();
});

错误追踪与调试

增强异步错误的可调试性:

  1. 添加上下文信息
async function wrappedFetch(url) {
  try {
    return await fetch(url);
  } catch (err) {
    err.context = { url, timestamp: Date.now() };
    throw err;
  }
}
  1. 长期任务监控
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));
});

异步模式演进

从历史角度看待异步处理发展:

  1. 回调地狱时期
// 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('完成!');
    });
  });
});
  1. 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);
  1. 现代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);
  }
}

实用工具函数

收集常用的异步辅助函数:

  1. 重试机制
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);
  }
}
  1. 进度通知
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配合的异步模式:

  1. 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);
  }
};
  1. 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

上一篇:模块化开发规范

下一篇:性能优化建议

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌