深入理解ES6 Promise

张开发
2026/4/13 18:11:55 15 分钟阅读

分享文章

深入理解ES6 Promise
一、为什么需要 Promise在 JavaScript 中异步操作如网络请求、文件读取、定时器通常通过回调函数处理。但多个异步操作依赖或串行时会出现回调地狱// 回调地狱示例setTimeout((){console.log(第一步);setTimeout((){console.log(第二步);setTimeout((){console.log(第三步);},1000);},1000);},1000);回调地狱的缺点代码横向增长可读性差难以捕获错误难以并行或竞速处理多个异步任务Promise 正是为了解决这些问题而诞生的它让异步代码更接近同步写法的风格且提供了统一的错误处理机制。二、Promise 是什么Promise 是 ES6 新增的一个内置构造函数代表一个异步操作的最终完成或失败及其结果值。字面理解Promise 是一个“承诺”承诺在未来某个时刻会给你一个结果成功或失败。一个 Promise 对象处于以下三种状态之一状态含义特点pending初始状态未完成也未拒绝可以转变为fulfilled或rejectedfulfilled操作成功完成有一个value结果值rejected操作失败有一个reason失败原因状态一旦改变就永久固定不会再变。三、Promise 的基本用法1. 创建一个 Promise使用new Promise(executor)其中executor是一个立即执行的函数它接收两个参数resolve和reject。constpromisenewPromise((resolve,reject){// 异步操作constsuccesstrue;setTimeout((){if(success){resolve(成功的数据);// 将状态改为 fulfilled}else{reject(失败的原因);// 将状态改为 rejected}},1000);});2. 使用.then()和.catch()消费 Promisepromise.then((value){console.log(成功,value);}).catch((error){console.error(失败,error);});.then()接收两个可选参数onFulfilled和onRejected。通常只传第一个错误交给.catch()。.catch()是.then(null, onRejected)的语法糖。3..finally()—— 无论成功失败都会执行promise.then(valueconsole.log(value)).catch(errorconsole.error(error)).finally(()console.log(操作结束清理资源));四、Promise 的链式调用核心特性.then()或.catch()返回一个新的 Promise因此可以串联多个异步操作。newPromise((resolve){resolve(1);}).then((value){console.log(value);// 1returnvalue1;}).then((value){console.log(value);// 2returnvalue1;}).then((value){console.log(value);// 3});链式调用中的返回值处理规则如果回调函数返回一个普通值包括undefined它会自动被包装成一个fulfilled的 Promise。如果返回一个 Promise那么下一个.then()会等待这个 Promise 的结果。如果抛出异常throw new Error()会进入下一个.catch()。示例模拟异步任务顺序执行functionasyncTask(name,duration){returnnewPromise((resolve){setTimeout((){console.log(${name}完成);resolve(name);},duration);});}asyncTask(任务1,1000).then(()asyncTask(任务2,500)).then(()asyncTask(任务3,800)).then(()console.log(所有任务完成));五、Promise 的静态方法1.Promise.resolve(value)快速返回一个状态为fulfilled的 Promise。Promise.resolve(直接成功).then(console.log);// 直接成功如果value本身是一个 Promise则直接返回该 Promise。2.Promise.reject(reason)快速返回一个状态为rejected的 Promise。Promise.reject(出错了).catch(console.error);// 出错了3.Promise.all(iterable)并行执行多个 Promise所有都成功才成功结果按顺序组成数组任何一个失败则立即失败并返回第一个失败的原因。constp1Promise.resolve(1);constp2Promise.resolve(2);constp3newPromise((resolve)setTimeout(resolve,1000,3));Promise.all([p1,p2,p3]).then((results){console.log(results);// [1, 2, 3] (等待 1 秒后输出)});失败示例constp1Promise.resolve(1);constp2Promise.reject(错误);Promise.all([p1,p2]).catch((err){console.log(err);// 错误});4.Promise.race(iterable)竞速返回最先 settled成功或失败的那个 Promise 的结果。constp1newPromise((resolve)setTimeout(resolve,500,慢));constp2newPromise((resolve)setTimeout(resolve,100,快));Promise.race([p1,p2]).then(console.log);// 快常用于设置超时functionfetchWithTimeout(url,timeout5000){constfetchPromisefetch(url);consttimeoutPromisenewPromise((_,reject)setTimeout(()reject(newError(请求超时)),timeout));returnPromise.race([fetchPromise,timeoutPromise]);}5.Promise.allSettled(iterable)(ES2020)等待所有 Promise 都 settle成功或失败返回每个对象的状态和结果或原因。永远不会进入.catch()。constpromises[Promise.resolve(成功),Promise.reject(失败),Promise.resolve(另一个成功),];Promise.allSettled(promises).then((results){console.log(results);/* [ { status: fulfilled, value: 成功 }, { status: rejected, reason: 失败 }, { status: fulfilled, value: 另一个成功 } ] */});6.Promise.any(iterable)(ES2021)只要有一个 Promise 成功就返回那个成功的结果如果所有都失败则返回一个AggregateError聚合错误。constp1Promise.reject(错误1);constp2Promise.reject(错误2);constp3Promise.resolve(成功);Promise.any([p1,p2,p3]).then(console.log);// 成功全部失败的情况Promise.any([Promise.reject(a),Promise.reject(b)]).catch((err){console.log(errinstanceofAggregateError);// trueconsole.log(err.errors);// [a, b]});六、错误处理的最佳实践总是添加.catch()或者在每个.then()的第二个参数中处理错误否则错误会被静默吞没虽然浏览器会报 unhandled rejection但不推荐依赖于此。在链式调用的末尾统一捕获错误doSomething().then(doSomethingElse).then(doThirdThing).catch(handleError);// 捕获前面任意一步的错误不要在.then()中抛出普通字符串而应该抛出Error对象以便获得堆栈信息。async/await与 try-catch是 Promise 的语法糖但底层依然是 Promise理解 Promise 是掌握async/await的基础。七、Promise 与回调地狱对比回调地狱getUser((user){getPosts(user.id,(posts){getComments(posts[0].id,(comments){console.log(comments);});});});Promise 版本假设每个函数都返回 PromisegetUser().then(usergetPosts(user.id)).then(postsgetComments(posts[0].id)).then(commentsconsole.log(comments)).catch(errorconsole.error(error));扁平化更易读。八、进阶示例串行与并行控制1. 串行执行一组异步任务逐个进行consttasks[task1,task2,task3];// 每个 task 返回 Promisetasks.reduce((prevPromise,task){returnprevPromise.then(()task());},Promise.resolve());2. 限制并发数量例如同时最多 3 个请求asyncfunctionlimitConcurrent(tasks,limit){constresults[];constexecuting[];for(consttaskoftasks){constptask().then(rresults.push(r));executing.push(p);if(executing.lengthlimit){awaitPromise.race(executing);// 清除已完成的任务executing.splice(executing.findIndex(eep),1);}}awaitPromise.all(executing);returnresults;}九、Promise 的局限性问题说明不可取消一旦创建 Promise无法从外部取消它的执行某些库提供 cancelable promise但非标准。无法获取中间状态无法同步获取“进度”只能拿到最终结果不过可以自己实现进度通知。错误可能被吞没若没有.catch()且未被async/await捕获错误将静默消失现代浏览器会打印 unhandledrejection 警告。新手容易误解例如忘记return导致链断裂或在.then()中创建新的 Promise 但未返回。十、总结Promise 是异步编程的基石它将回调函数的嵌套转化为链式调用。核心掌握状态pending/fulfilled/rejected、链式调用、静态方法all、race、allSettled、any。搭配async/await使用时Promise 依然在幕后工作理解 Promise 有助于写出更可靠的异步代码。实际开发中几乎所有现代 JavaScript API如fetch、fs.promises、setTimeout包装都基于 Promise。

更多文章