现代JavaScript异步编程完全指南 异步编程是JavaScript的核心特性之一,也是很多初学者的难点。从回调地狱到Promise,再到Async/Await,JavaScript的异步编程方式不断进化。今天,我将带你深入理解JavaScript异步编程的方方面面。
目录 异步编程基础 什么是异步编程? 同步编程:代码按顺序执行,每一行代码都必须等待前一行执行完成。
异步编程:代码执行时,不需要等待长时间操作完成,可以继续执行其他代码。
console .log ('开始' );const result = longRunningTask (); console .log ('结果:' , result);console .log ('结束' );console .log ('开始' );setTimeout (() => { console .log ('异步任务完成' ); }, 1000 ); console .log ('结束' );
输出:
为什么需要异步编程? JavaScript是单线程的,如果所有操作都是同步的,长时间运行的任务会阻塞整个程序,导致用户界面卡死。
异步编程允许我们在等待I/O操作(网络请求、文件读写等)时,继续执行其他代码。
JavaScript事件循环 JavaScript的事件循环是实现异步的关键:
graph TD A[调用栈 Call Stack] --> B{任务类型} B -->|同步| C[执行] B -->|异步| D[Web APIs] D --> E[回调队列 Callback Queue] E --> F[事件循环 Event Loop] F --> G{调用栈为空?} G -->|是| H[执行回调] G -->|否| I[继续等待]
回调函数 基本概念 回调函数是作为参数传递给另一个函数的函数,在某个操作完成后被调用。
function fetchData (callback ) { setTimeout (() => { const data = { id : 1 , name : 'Product' }; callback (null , data); }, 1000 ); } fetchData ((err, data ) => { if (err) { console .error ('错误:' , err); } else { console .log ('数据:' , data); } });
回调地狱(Callback Hell) 当多个异步操作嵌套时,代码会变得难以维护:
function badOrder ( ) { getOrder (orderId, (order ) => { getUser (order.userId , (user ) => { getProduct (order.productId , (product ) => { getInventory (product.inventoryId , (inventory ) => { }); }); }); }); }
解决回调地狱的方法 1. 提取函数 function badOrder ( ) { getOrder (orderId, handleOrder); } function handleOrder (order ) { getUser (order.userId , handleUser); } function handleUser (user ) { }
2. 使用事件发射器 const EventEmitter = require ('events' );class OrderProcessor extends EventEmitter { processOrder (orderId ) { this .emit ('order-start' , orderId); getOrder (orderId, (order ) => { this .emit ('order-received' , order); }); } }
Promise详解 Promise基础 Promise是一个对象,代表一个异步操作的最终完成或失败。
const myPromise = new Promise ((resolve, reject ) => { setTimeout (() => { const success = true ; if (success) { resolve ('操作成功!' ); } else { reject (new Error ('操作失败' )); } }, 1000 ); }); myPromise .then (result => console .log (result)) .catch (error => console .error (error));
Promise的状态 pending : 初始状态fulfilled : 操作成功rejected : 操作失败const promise = new Promise ((resolve ) => { resolve ('成功' ); }); console .log (promise); setTimeout (() => { console .log (promise); }, 100 );
Promise方法 Promise.all const promise1 = Promise .resolve (3 );const promise2 = 42 ;const promise3 = new Promise ((resolve ) => setTimeout (resolve, 100 , 'foo' ));Promise .all ([promise1, promise2, promise3]) .then ((values ) => { console .log (values); });
特点 :
等待所有Promise完成 如果任何一个Promise失败,整个Promise.all失败 按数组顺序返回结果 Promise.allSettled const promises = [ Promise .resolve (42 ), Promise .reject ('错误' ), Promise .resolve ('成功' ) ]; Promise .allSettled (promises) .then (results => { console .log (results); });
Promise.race const promise1 = new Promise (resolve => setTimeout (resolve, 500 , 'one' ));const promise2 = new Promise (resolve => setTimeout (resolve, 100 , 'two' ));Promise .race ([promise1, promise2]) .then (value => { console .log (value); });
Promise.any const promises = [ Promise .reject ('第一个失败' ), Promise .resolve ('成功' ) ]; Promise .any (promises) .then (value => { console .log (value); }) .catch (error => { console .error (error.errors ); });
实用Promise模式 1. 串行执行 function sequentialPromises (promises ) { return promises.reduce ((chain, promise ) => { return chain.then (result => { return Promise .resolve (promise).then (newResult => { return result.concat (newResult); }); }); }, Promise .resolve ([])); } const promises = [ () => new Promise (resolve => setTimeout (() => resolve (1 ), 100 )), () => new Promise (resolve => setTimeout (() => resolve (2 ), 100 )), () => new Promise (resolve => setTimeout (() => resolve (3 ), 100 )) ]; sequentialPromises (promises) .then (results => console .log (results));
2. 并行控制 function promisePool (promises, limit ) { return new Promise ((resolve ) => { const results = []; let activeCount = 0 ; let completedCount = 0 ; function next ( ) { while (activeCount < limit && promises.length > 0 ) { activeCount++; const promise = promises.shift (); promise () .then (result => { results.push (result); }) .catch (error => { results.push ({ error }); }) .finally (() => { activeCount--; completedCount++; if (completedCount === promises.length + activeCount) { resolve (results); } else { next (); } }); } } next (); }); } const taskPromises = [ () => new Promise (resolve => setTimeout (() => resolve ('Task 1' ), 1000 )), () => new Promise (resolve => setTimeout (() => resolve ('Task 2' ), 1000 )), () => new Promise (resolve => setTimeout (() => resolve ('Task 3' ), 1000 )), () => new Promise (resolve => setTimeout (() => resolve ('Task 4' ), 1000 )) ]; promisePool (taskPromises, 2 ) .then (results => console .log (results));
Async/Await语法 基础语法 function fetchUser ( ) { return fetch ('/api/user' ) .then (response => response.json ()) .then (data => data.user ) .catch (error => console .error (error)); } async function fetchUser ( ) { try { const response = await fetch ('/api/user' ); const data = await response.json (); return data.user ; } catch (error) { console .error (error); } }
Async/Await的优势 代码可读性 :看起来像同步代码错误处理 :使用try/catch,更直观调试友好 :可以像调试同步代码一样调试Async/Await的高级用法 1. 并行执行 async function serial ( ) { const user = await fetchUser (); const posts = await fetchPosts (user.id ); const comments = await fetchComments (posts[0 ].id ); return { user, posts, comments }; } async function parallel ( ) { const [user, posts, comments] = await Promise .all ([ fetchUser (), fetchPosts (), fetchComments () ]); return { user, posts, comments }; }
2. 条件执行 async function conditionalFetch (id ) { if (id > 0 ) { const response = await fetch (`/api/item/${id} ` ); return await response.json (); } else { return { error : 'Invalid ID' }; } }
3. 循环中的异步操作 async function sequentialFetch ( ) { const ids = [1 , 2 , 3 , 4 , 5 ]; const results = []; for (const id of ids) { const result = await fetchItem (id); results.push (result); } return results; } async function parallelFetch ( ) { const ids = [1 , 2 , 3 , 4 , 5 ]; const promises = ids.map (id => fetchItem (id)); return await Promise .all (promises); } async function limitedFetch ( ) { const ids = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]; const results = []; const limit = 3 ; for (let i = 0 ; i < ids.length ; i += limit) { const batch = ids.slice (i, i + limit); const batchResults = await Promise .all ( batch.map (id => fetchItem (id)) ); results.push (...batchResults); } return results; }
高级异步模式 1. 生成器函数 function * asyncGenerator ( ) { try { const result1 = yield fetch ('/api/endpoint1' ); const result2 = yield fetch ('/api/endpoint2' , result1); return result2; } catch (error) { console .error (error); } } function run (generator ) { const iterator = generator (); function handleNext (result ) { const { value, done } = iterator.next (result); if (!done) { if (value instanceof Promise ) { value.then (handleNext).catch (iterator.throw .bind (iterator)); } else { handleNext (value); } } } handleNext (); } run (asyncGenerator);
2. Async Iterator async function * asyncRange (start, end ) { for (let i = start; i <= end; i++) { await new Promise (resolve => setTimeout (resolve, 100 )); yield i; } } (async () => { for await (const num of asyncRange (1 , 5 )) { console .log (num); } })();
3. Promise和EventEmitter结合 const { EventEmitter } = require ('events' );class AsyncEmitter extends EventEmitter { constructor ( ) { super (); this .promises = new Map (); } async emit (eventName, ...args ) { const handlers = this .listeners (eventName); if (handlers.length === 0 ) { return false ; } const promise = Promise .all ( handlers.map (handler => { return new Promise ((resolve, reject ) => { try { resolve (handler.apply (this , args)); } catch (error) { reject (error); } }); }) ); this .promises .set (eventName, promise); return true ; } async once (eventName ) { return new Promise ((resolve, reject ) => { const handler = (...args ) => { this .removeListener (eventName, handler); resolve (args); }; this .on (eventName, handler); }); } }
并发控制 1. 基本并发控制 class ConcurrencyController { constructor (maxConcurrent = 5 ) { this .maxConcurrent = maxConcurrent; this .current = 0 ; this .queue = []; } async execute (task ) { return new Promise ((resolve, reject ) => { this .queue .push ({ task, resolve, reject }); this .process (); }); } async process ( ) { if (this .current >= this .maxConcurrent || this .queue .length === 0 ) { return ; } this .current ++; const { task, resolve, reject } = this .queue .shift (); try { const result = await task (); resolve (result); } catch (error) { reject (error); } finally { this .current --; this .process (); } } } const controller = new ConcurrencyController (3 );const tasks = Array (10 ).fill ().map ((_, i ) => () => new Promise (resolve => { setTimeout (() => { console .log (`Task ${i} completed` ); resolve (i); }, 1000 ); }) ); tasks.forEach (task => controller.execute (task));
2. 批量处理 async function batchProcess (items, batchSize, processor ) { const results = []; for (let i = 0 ; i < items.length ; i += batchSize) { const batch = items.slice (i, i + batchSize); const batchResults = await Promise .all ( batch.map (item => processor (item)) ); results.push (...batchResults); } return results; } const items = Array (100 ).fill ().map ((_, i ) => i + 1 );batchProcess (items, 10 , async (item) => { return new Promise (resolve => { setTimeout (() => { resolve (item * 2 ); }, Math .random () * 1000 ); }); }).then (results => { console .log ('处理完成:' , results); });
3. 节流控制 class Throttle { constructor (limit, interval ) { this .limit = limit; this .interval = interval; this .queue = []; this .active = 0 ; this .timer = null ; } async execute (task ) { return new Promise ((resolve, reject ) => { this .queue .push ({ task, resolve, reject }); this .process (); }); } async process ( ) { if (this .active >= this .limit || this .queue .length === 0 ) { return ; } this .active ++; const { task, resolve, reject } = this .queue .shift (); try { const result = await task (); resolve (result); } catch (error) { reject (error); } finally { this .active --; if (this .timer ) { clearTimeout (this .timer ); } this .timer = setTimeout (() => { this .process (); }, this .interval ); } } }
错误处理 1. 基本错误处理 fetchData () .then (data => { return processData (data); }) .catch (error => { console .error ('处理失败:' , error); }); async function fetchDataAndProcess ( ) { try { const data = await fetchData (); const processed = await processData (data); return processed; } catch (error) { console .error ('处理失败:' , error); throw error; } }
2. 多重错误捕获 async function multipleOperations ( ) { const errors = []; const results = []; const operations = [ fetchData1, fetchData2, fetchData3 ]; for (const operation of operations) { try { const result = await operation (); results.push (result); } catch (error) { errors.push (error); console .error (`操作失败: ${error.message} ` ); } } if (errors.length > 0 ) { console .warn (`${errors.length} 个操作失败,${results.length} 个操作成功` ); } return { results, errors }; }
3. 错误重试机制 async function retryOperation (operation, maxRetries = 3 , delay = 1000 ) { let lastError; for (let i = 0 ; i < maxRetries; i++) { try { return await operation (); } catch (error) { lastError = error; console .warn (`操作失败,${maxRetries - i - 1 } 次重试机会:` , error.message ); if (i < maxRetries - 1 ) { await new Promise (resolve => setTimeout (resolve, delay)); } } } throw lastError; } retryOperation ( () => fetchWithRetry (), 5 , 2000 ).then (result => { console .log ('最终成功:' , result); }).catch (error => { console .error ('所有重试都失败:' , error); });
4. 错误分类处理 class ApiError extends Error { constructor (message, statusCode, code ) { super (message); this .statusCode = statusCode; this .code = code; this .isOperational = true ; } } async function fetchWith 分类处理() { try { const response = await fetch ('/api/data' ); if (!response.ok ) { const error = await response.json (); throw new ApiError ( error.message , response.status , error.code ); } return await response.json (); } catch (error) { if (error instanceof ApiError ) { switch (error.statusCode ) { case 400 : console .error ('请求参数错误:' , error.message ); break ; case 401 : console .error ('未授权访问' ); break ; case 404 : console .error ('资源不存在' ); break ; case 500 : console .error ('服务器错误' ); break ; default : console .error ('未知错误:' , error); } } else { console .error ('网络错误:' , error); } throw error; } }
性能优化 1. 避免不必要的等待 async function badSequential ( ) { const data1 = await fetch ('/api/data1' ); const data2 = await fetch ('/api/data2' ); const data3 = await fetch ('/api/data3' ); return { data1, data2, data3 }; } async function goodParallel ( ) { const [data1, data2, data3] = await Promise .all ([ fetch ('/api/data1' ), fetch ('/api/data2' ), fetch ('/api/data3' ) ]); return { data1, data2, data3 }; }
2. 使用缓存 class AsyncCache { constructor (ttl = 60000 ) { this .cache = new Map (); this .ttl = ttl; } async get (key, fetcher ) { const cached = this .cache .get (key); if (cached && Date .now () - cached.timestamp < this .ttl ) { return cached.value ; } const value = await fetcher (); this .cache .set (key, { value, timestamp : Date .now () }); return value; } clear ( ) { this .cache .clear (); } } const cache = new AsyncCache (5000 ); async function getData (id ) { return cache.get (id, () => fetch (`/api/data/${id} ` ).then (res => res.json ())); }
3. 批量请求 class BatchRequest { constructor (batchSize = 5 , delay = 100 ) { this .batchSize = batchSize; this .delay = delay; this .queue = []; this .timer = null ; } async request (url, options = {} ) { return new Promise ((resolve, reject ) => { this .queue .push ({ url, options, resolve, reject }); this .scheduleBatch (); }); } scheduleBatch ( ) { if (this .timer ) return ; this .timer = setTimeout (() => { this .processBatch (); }, this .delay ); } async processBatch ( ) { if (this .queue .length === 0 ) { this .timer = null ; return ; } const batch = this .queue .splice (0 , this .batchSize ); try { const promises = batch.map (({ url, options } ) => fetch (url, options).then (res => res.json ()) ); const results = await Promise .all (promises); batch.forEach ((item, index ) => { item.resolve (results[index]); }); } catch (error) { batch.forEach (item => { item.reject (error); }); } finally { this .scheduleBatch (); } } }
4. 懒加载 class AsyncLoader { constructor ( ) { this .cache = new Map (); this .loading = new Map (); } async load (moduleName, loader ) { if (this .cache .has (moduleName)) { return this .cache .get (moduleName); } if (this .loading .has (moduleName)) { return this .loading .get (moduleName); } const loadPromise = loader ().then (module => { this .cache .set (moduleName, module ); this .loading .delete (moduleName); return module ; }); this .loading .set (moduleName, loadPromise); return loadPromise; } } const loader = new AsyncLoader ();async function loadComponent (name ) { return loader.load (name, () => import (`./components/${name} .js` )); } loadComponent ('Button' ).then (Button => { const button = new Button (); document .body .appendChild (button); });
实战案例 案例1:实现图片懒加载组件 class LazyImage { constructor (selector ) { this .images = document .querySelectorAll (selector); this .observer = null ; this .init (); } init ( ) { this .observer = new IntersectionObserver ((entries ) => { entries.forEach (entry => { if (entry.isIntersecting ) { const img = entry.target ; const src = img.dataset .src ; if (src) { img.src = src; img.classList .add ('loaded' ); this .observer .unobserve (img); } } }); }, { rootMargin : '50px 0px' , threshold : 0.1 }); this .images .forEach (img => { this .observer .observe (img); }); } destroy ( ) { if (this .observer ) { this .observer .disconnect (); } } } new LazyImage ('img[data-src]' );
案例2:实现无限滚动加载 class InfiniteScroll { constructor (options = {} ) { this .container = options.container || document .querySelector ('#content' ); this .url = options.url ; this .template = options.template ; this .threshold = options.threshold || 100 ; this .loading = false ; this .page = 1 ; this .hasMore = true ; this .init (); } init ( ) { window .addEventListener ('scroll' , this .handleScroll .bind (this )); this .loadMore (); } handleScroll ( ) { if (this .loading || !this .hasMore ) return ; const scrollPosition = window .innerHeight + window .scrollY ; const thresholdPosition = document .body .offsetHeight - this .threshold ; if (scrollPosition >= thresholdPosition) { this .loadMore (); } } async loadMore ( ) { if (this .loading ) return ; this .loading = true ; this .showLoadingIndicator (); try { const response = await fetch (`${this .url} ?page=${this .page} ` ); const data = await response.json (); if (data.items .length === 0 ) { this .hasMore = false ; return ; } data.items .forEach (item => { const element = this .template (item); this .container .appendChild (element); }); this .page ++; } catch (error) { console .error ('加载失败:' , error); } finally { this .loading = false ; this .hideLoadingIndicator (); } } showLoadingIndicator ( ) { const indicator = document .createElement ('div' ); indicator.className = 'loading-indicator' ; indicator.textContent = '加载中...' ; this .container .appendChild (indicator); this .indicator = indicator; } hideLoadingIndicator ( ) { if (this .indicator ) { this .indicator .remove (); this .indicator = null ; } } destroy ( ) { window .removeEventListener ('scroll' , this .handleScroll ); if (this .indicator ) { this .indicator .remove (); } } } const infiniteScroll = new InfiniteScroll ({ container : document .querySelector ('#content' ), url : '/api/articles' , template : (article ) => { const div = document .createElement ('div' ); div.className = 'article' ; div.innerHTML = ` <h2>${article.title} </h2> <p>${article.excerpt} </p> ` ; return div; } });
案例3:实现文件上传进度条 class FileUploader { constructor (options = {} ) { this .endpoint = options.endpoint || '/api/upload' ; this .onProgress = options.onProgress || (() => {}); this .onComplete = options.onComplete || (() => {}); this .onError = options.onError || (() => {}); this .onAbort = options.onAbort || (() => {}); this .maxRetries = options.maxRetries || 3 ; this .retryDelay = options.retryDelay || 1000 ; } async upload (file, additionalData = {} ) { const formData = new FormData (); formData.append ('file' , file); Object .entries (additionalData).forEach (([key, value] ) => { formData.append (key, value); }); let retryCount = 0 ; let lastError; while (retryCount <= this .maxRetries ) { try { const response = await fetch (this .endpoint , { method : 'POST' , body : formData, onUploadProgress : (progressEvent ) => { const percentCompleted = Math .round ( (progressEvent.loaded * 100 ) / progressEvent.total ); this .onProgress (percentCompleted); } }); if (!response.ok ) { throw new Error (`Upload failed: ${response.statusText} ` ); } const result = await response.json (); this .onComplete (result); return result; } catch (error) { lastError = error; retryCount++; if (retryCount <= this .maxRetries ) { console .log (`Retrying upload (${retryCount} /${this .maxRetries} )...` ); await new Promise (resolve => setTimeout (resolve, this .retryDelay )); } else { this .onError (error); throw error; } } } } abort (controller ) { controller.abort (); this .onAbort (); } } const uploader = new FileUploader ({ endpoint : '/api/upload' , onProgress : (percent ) => { console .log (`上传进度: ${percent} %` ); document .getElementById ('progress' ).style .width = `${percent} %` ; }, onComplete : (result ) => { console .log ('上传完成:' , result); alert ('文件上传成功!' ); }, onError : (error ) => { console .error ('上传失败:' , error); alert ('文件上传失败,请重试' ); } }); document .getElementById ('fileInput' ).addEventListener ('change' , async (e) => { const file = e.target .files [0 ]; if (file) { try { const result = await uploader.upload (file, { userId : '123' , category : 'documents' }); console .log ('上传结果:' , result); } catch (error) { console .error ('上传失败:' , error); } } });
案例4:实现WebSocket连接管理 class WebSocketManager { constructor (url, options = {} ) { this .url = url; this .reconnectInterval = options.reconnectInterval || 3000 ; this .maxReconnectAttempts = options.maxReconnectAttempts || 5 ; this .onMessage = options.onMessage || (() => {}); this .onOpen = options.onOpen || (() => {}); this .onClose = options.onClose || (() => {}); this .onError = options.onError || (() => {}); this .ws = null ; this .reconnectAttempts = 0 ; this .isConnecting = false ; this .messageQueue = []; this .shouldReconnect = true ; this .connect (); } connect ( ) { if (this .isConnecting || this .ws ?.readyState === WebSocket .OPEN ) { return ; } this .isConnecting = true ; this .ws = new WebSocket (this .url ); this .ws .onopen = () => { console .log ('WebSocket连接已建立' ); this .reconnectAttempts = 0 ; this .isConnecting = false ; this .onOpen (); this .flushMessageQueue (); }; this .ws .onmessage = (event ) => { try { const data = JSON .parse (event.data ); this .onMessage (data); } catch (error) { console .error ('解析消息失败:' , error); this .onError (error); } }; this .ws .onclose = () => { console .log ('WebSocket连接已关闭' ); this .isConnecting = false ; this .onClose (); if (this .shouldReconnect && this .reconnectAttempts < this .maxReconnectAttempts ) { console .log (`尝试重新连接 (${this .reconnectAttempts + 1 } /${this .maxReconnectAttempts} )...` ); setTimeout (() => { this .reconnectAttempts ++; this .connect (); }, this .reconnectInterval ); } }; this .ws .onerror = (error ) => { console .error ('WebSocket错误:' , error); this .isConnecting = false ; this .onError (error); }; } send (data ) { if (this .ws ?.readyState === WebSocket .OPEN ) { this .ws .send (JSON .stringify (data)); } else { this .messageQueue .push (data); } } flushMessageQueue ( ) { while (this .messageQueue .length > 0 ) { const message = this .messageQueue .shift (); this .send (message); } } disconnect ( ) { this .shouldReconnect = false ; if (this .ws ) { this .ws .close (); } this .messageQueue = []; } reconnect ( ) { this .disconnect (); this .reconnectAttempts = 0 ; this .connect (); } } const wsManager = new WebSocketManager ('wss://api.example.com/ws' , { onMessage : (data ) => { console .log ('收到消息:' , data); switch (data.type ) { case 'chat' : break ; case 'notification' : break ; case 'update' : break ; } }, onOpen : () => { console .log ('连接成功,发送认证消息' ); wsManager.send ({ type : 'auth' , token : localStorage .getItem ('token' ) }); }, onClose : () => { console .log ('连接关闭' ); }, onError : (error ) => { console .error ('连接错误:' , error); } }); document .getElementById ('sendButton' ).addEventListener ('click' , () => { const message = document .getElementById ('messageInput' ).value ; if (message) { wsManager.send ({ type : 'chat' , message : message }); } });
总结 现代JavaScript异步编程已经发展得非常成熟,从回调函数到Promise,再到Async/Await,每种方式都有其适用场景。
核心概念回顾 事件循环 :JavaScript异步执行的基础回调函数 :最早的异步处理方式Promise :改进的异步处理机制Async/Await :更直观的异步语法并发控制 :管理多个异步操作错误处理 :优雅地处理异步错误性能优化 :提高异步操作效率最佳实践 使用Async/Await :代码更易读、易维护合理使用Promise :避免不必要的Promise包装错误处理 :始终处理异步操作中的错误性能考虑 :合理使用并行和串行操作资源管理 :适时清理不需要的异步操作学习建议 理解基础 :深入理解事件循环机制实践为主 :通过实际项目练习学习高级模式 :掌握Generator、Async Iterator等关注性能 :学习并发控制和性能优化技巧异步编程是JavaScript开发者必须掌握的核心技能,希望这份指南能帮助你更好地理解和应用现代JavaScript异步编程技术。
最后更新:2026年5月18日 分类:IT | 知识学习