Web前端入门第 69 问:JavaScript Promise 提供的方法都使用过吗?
Promise 这个 API 曾在 JS 领域掀起过血雨腥风,以前的大佬们都喜欢手搓一个自己的 Promise 用以理解 Promise 的原理。
Promise 的诞生,应该多少都有受到 jQuery 的异步方法 $.Deferred() 影响。
应用场景
Promise 唯一作用就是在处理异步耗时任务的时候,不要出现回调地狱。在没有 Promise 之前,一般使用 callback 来解决异步问题,一般代码都是这样:
a(() => {
b(() => {
c(() => {
d(() => {})
})
})
})
就这样一层一层套进去,像套娃一样的回调方法,这就是所谓的 回调地狱。
使用 Promise 之后,代码就可以改成链式调用,避免了回调地狱:
a()
.then(() => {
return b()
}).then(() => {
return c()
}).then(() => {
return d()
})
常见用法
在代码中经常会看到这样使用 Promise:
function task() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('耗时任务执行成功')
}, 1000)
})
}
// 开始执行耗时任务
task().then(res => {
console.log('任务执行成功')
}).catch(err => {
console.log('任务执行失败')
}).finally(() => {
console.log('任务执行完成')
})
// 或者是这样
task().then(res => {
console.log('任务执行成功')
}, err => {
console.log('任务执行失败')
}).finally(() => {
console.log('任务执行完成')
})
.then 方法接收两个参数,第一个参数是执行成功(fulfilled)的回调方法,第二个参数是执行失败(rejected)的回调方法。
.catch 方法用于捕获 Promise 中的错误,如果 Promise 执行失败,就会执行 .catch 方法,比如:
function task() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('任务执行失败'))
}, 1000)
})
}
task().catch(err => {
console.log(err.message)
})
.finally 方法无论 Promise 执行成功与否,都会执行的方法,一般多用于关闭 loading 这种效果,也可以用于清理资源。
Promise 的静态方法
以上三个方法都是 Promise 的实例方法,除了常用的实例方法外,Promise 还提供了一些静态方法,这些静态方法不是很常用(也可能是咱的段位太低),但在某些特定的需求场景中也是很有用的利器。
Promise.reject() 与 Promise.resolve()
这一对静态方法一般多用于将同步方法改成 Promise,比如:
function task() {
if (Math.random() > 0.5) {
return Promise.reject(new Error('任务执行失败'))
}
return Promise.resolve('任务执行成功')
}
task().then((res) => console.log(res), err => console.error(err.message))
其参数还支持返回一个 Promise 对象或者一个 thenable 对象。比如这样:
function task1() {
return new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
return resolve('耗时任务执行成功')
}
return reject(new Error('任务执行失败'))
})
}
function task2() {
// Promise 对象
return Promise.resolve(task1())
}
task2().then((res) => console.log(res), err => console.error(err.message))
// ----------------------
function task3() {
// thenable 对象
const thenable = {
then(onFulfill, onReject) {
if (Math.random() > 0.5) {
return onReject(new Error('任务执行失败'))
}
return onFulfill('任务执行成功')
},
}
return Promise.resolve(thenable)
}
task3().then((res) => console.log(res), err => console.error(err.message))
Promise.all()
用于同时处理多个 Promise,如果全部都成功解决时,返回的 Promise 才会解决,但凡有一个被拒绝,则返回的 Promise 失败。
const p1 = Promise.resolve('1')
const p2 = Promise.reject('2')
const p3 = Promise.resolve('3')
Promise.all([p1, p3]).then(res => {
console.log('成功', res) // ['1', '3']
}).catch(err => {
console.error('失败', err)
})
Promise.all([p2, p3]).then(res => {
console.log('成功', res)
}).catch(err => {
console.error('失败', err) // 获得失败的返回值 2
})
Promise.allSettled()
与 Promise.all 有点不同,这个静态方法会等到所有的 Promise 都解决或者失败,然后返回一个 Promise,这个 Promise 的结果是一个数组,数组的元素是所有 Promise 的状态及响应结果。一般多用于多个接口同时请求场景,可以容忍部分接口异常的情况。
const p1 = Promise.resolve('1')
const p2 = Promise.reject('用于测试失败')
const p3 = Promise.resolve('3')
Promise.allSettled([p1, p2, p3]).then(res => {
res.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.error('失败:', result.reason);
}
});
})
Promise.any()
也是用于处理多个 Promise,此方法的逻辑是:只获取第一个成功的 Promise 返回结果,如果全部失败,则返回一个失败的 Promise。
const p1 = new Promise((resolve, reject) => setTimeout(reject, 100, '第一个失败'));
const p2 = new Promise((resolve) => setTimeout(resolve, 200, '第二个成功'));
const p3 = new Promise((resolve, reject) => setTimeout(reject, 300, '第三个失败'));
const p4 = new Promise((resolve) => setTimeout(resolve, 200, '第四个成功'));
Promise.any([p1, p2, p3, p4]).then(res => console.log('成功:', res)); // 成功: 第二个成功
Promise.any([p1, p3]).catch(error => {
console.error('所有 Promise 失败:', error) // AggregateError: All promises were rejected
console.error('失败原因:', error.errors) // ['第一个失败', '第三个失败']
});
Promise.race()
此方法存在竞速的逻辑,谁最快返回就获得谁的结果,不论此结果是成功还是失败。
const p1 = new Promise((resolve, reject) => setTimeout(reject, 100, '第一个失败'));
const p2 = new Promise((resolve) => setTimeout(resolve, 100, '第二个成功'));
Promise.race([p1, p2])
.then(res => console.log('成功:', res))
.catch(error => console.error('失败:', error)); // 失败: 第一个失败
// p2 p1 交换位置,就会获得成功的结果
Promise.race([p2, p1])
.then(res => console.log('成功:', res)) // 成功: 第二个成功
.catch(error => {
console.error('失败:', error)
});
Promise.withResolvers()
2024 年新增的规范,使用时需注意兼容情况。
这方法相当于封装了一个语法糖,想比之前拥有了更简洁的代码逻辑而已,一般多用于跨模块共享 Promise 状态。使用方法:
const { promise, resolve, reject } = Promise.withResolvers();
function task() {
if (Math.random() > 0.5) {
return resolve('任务执行成功')
}
return reject(new Error('任务执行失败'))
}
task()
promise.then(res => {
console.log('成功:', res)
}).catch(err => {
console.error('失败:', err)
})
只是需要特别注意,promise 的状态在变为已解决或失败时,promise 的状态就无法再修改了,后面再调用 resolve 或 reject 方法都无任何响应。
这个静态方法可使用原有的方法实现,如下:
function createDeferredPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
const { promise, resolve, reject } = createDeferredPromise();
想比而言,withResolvers实现的代码更加简洁。
Promise.try()
这方法可时髦了,2025年才新增的规范,使用时需特别小心兼容性。
跟 try catch 相似,都是用于捕获异常,使用方法:
console.log(1)
Promise.try(() => {
console.log(3)
throw new Error('前端路引')
}).catch(err => console.log('捕获:', err))
console.log(2)
执行顺序:
1
3
2
捕获: Error: 前端路引
关于兼容性
由于 Promise 的静态方法都是在不同的 ES 版本迭代时添加进来的规范,所以多多少少都有一些兼容问题,在 Vite 项目中,可以使用以下两个插件来处理兼容问题:
1、@vitejs/plugin-legacy
周下载量在 35万左右
npm 地址:https://www.npmjs.com/package/@vitejs/plugin-legacy
2、vite-plugin-legacy-swc
周下载量再 1万左右
npm 地址:https://www.npmjs.com/package/vite-plugin-legacy-swc
使用方法可以参考 npm 的 Readme 文档。
写在最后
Promise 在处理异步任务时特别常用,还多用于一些耗时太长的任务场景,掌握 Promise 的使用,有利于编写出易于维护的项目代码。
Web前端入门第 69 问:JavaScript Promise 提供的方法都使用过吗?的更多相关文章
- web前端入坑第五篇:秒懂Vuejs、Angular、React原理和前端发展历史
秒懂Vuejs.Angular.React原理和前端发展历史 2017-04-07 小北哥哥 前端你别闹 今天来说说 "前端发展历史和框架" 「前端程序发展的历史」 「 不学自知, ...
- web前端入坑第二篇:web前端到底怎么学?干货资料! 【转】
http://blog.csdn.net/xllily_11/article/details/52145172 版权声明:本文为博主[小北]原创文章,如要转载请评论回复.个人前端公众号:前端你别闹,J ...
- web前端(13)—— 了解JavaScript,JavaScript的引入方式
从本篇博文开始,将进入web前端方便最关键最重要的部分——javascript,学到后面你就知道它真的太重要了 什么是JavaScript JavaScript一种直译式的脚本语言,是一种动态类型.弱 ...
- WEB前端工程师整理的原生JavaScript经典百例
一.原生JavaScript实现字符串长度截取 二.原生JavaScript获取域名主机 三.原生JavaScript转义html标签 四.原生JavaScript时间日期格式替换 Date.prot ...
- Web前端基础怎么学? JavaScript、html、css知识架构图
以前开发者只要掌握 HTML.CSS.JavaScript 三驾马车就能胜任一份前端的工作了.而现在除了普通的编码以外,还要考虑如何性能优化,如何跨端.跨平台实现功能,尤其是 AI.5G 技术的来临, ...
- web前端学习之HTML CSS/javascript之一
前端编码之路之坎坷,web前端应该一直是个战场吧,各种浏览器的不兼容,各种小细节的修改,要往一个好的产品经理方向走,实在是难,昨天听了一位十年经验的产品经理讲座,最重要的恐怕就是协调资源的能力,而协调 ...
- Android零基础入门第69节:ViewPager快速实现引导页
在很多APP第一次启动时都会出现引导页,在一些APP里面还会包括一些左右滑动翻页和页面轮播切换的情况.在之前也已经学习了AdapterViewFlipper和ViewFlipper,都可以很好的实现, ...
- web前端学习(四)JavaScript学习笔记部分(6)-- js内置对象
1.JS内置对象-什么是对象 1.1.什么是对象: JavaScript中的所有事物都是对象:字符串.数值.数组.函数 每个对象带有属性和方法 JavaScript允许自定义对象 1.2.自定义对象: ...
- 二十一、【.Net开源框架】EFW框架Web前端开发之目录结构和使用FireBug调试方法
回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.2:http://pan.baidu.com/s/1hcnuA EFW框架实例源代码下载:http://pan.baidu. ...
- Web前端入门教程之浏览器兼容问题及解决方法
JavaScript 被称为JS,是作为浏览器的内置脚本语言,为我们提供操控浏览器的能力,可以让网页呈现出各种特殊效果,为用户提供友好的互动体验.JS是Web前端入门教程中的重点和难点,而浏览器兼容性 ...
随机推荐
- 在 Hugging Face Spaces 上使用 Gradio 免费运行 ComfyUI 工作流
简介 在本教程中,我将逐步指导如何将一个复杂的 ComfyUI 工作流转换为一个简单的 Gradio 应用程序,并讲解如何将其部署在 Hugging Face Spaces 的 ZeroGPU 无服务 ...
- osmts:OERV之一站式管理测试脚本
最近团队里面实习的小伙伴开发了一个新的项目,可以用来一键式运行各种测试脚本并且完成数据总结,我也尝试部署了一下,遇到了一些问题,接下来一起解析一下这个项目. 首先是获取osmts git cl ...
- 【踩坑系列】使用httpclient调用第三方接口返回javax.net.ssl.SSLHandshakeException异常
1. 踩坑经历 最近做了个需求,需要调用第三方接口获取数据,在联调时一直失败,代码抛出javax.net.ssl.SSLHandshakeException异常, 具体错误信息如下所示: javax. ...
- 接口新特性--java进阶day03
1.接口新特性 在JDk8和JDK9开始,接口可以定义普通方法 这时就会感到很奇怪,明明之前说好接口只是用来制定规则的,为什么现在又可以定义普通方法了呢? 我们以一个公司案例进行讲解,公司1.0上线了 ...
- 【保姆级教程】windows 安装 docker 全流程
一.背景 许多小伙伴在安装 Dify 或是 RagFlow 这些工具的时候,往往会遇到一个难题,那就是 Docker 的安装. 首先,我们的PC安装的绝大部分是 Windows,但众所周知的原因,Wi ...
- 2024 (ICPC) Jiangxi Provincial Contest -- Official Contest
L. Campus 1.首先考虑时间复杂度,因为最多只会有2*k的时间点,所以我们采取的策略是,对这每个时刻,判断有多少扇门是开的,并且考虑这些门到其他点的最短路之和. 2.输入完数据以后,使用dij ...
- Spring的AoP(面向切面编程)
作用: 就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的 基础上,对我们的已有方法进行增强. 优势: 减少重复代码 提高开发效率 维护方便 AoP的实现方式:动态 ...
- 🎀git统计某段时间内代码的修改量/总代码量
1.前往git本地项目路径下 2.右键打开Git Bash工具 3.输入命令: 3.1.某段时间代码修改量 git log --since=2021-01-01 --until=2021-05-18 ...
- JAVA 线程实现/创建方式
每天记录一个知识点: 概要: java创建线程的方式: 继承Thread类 实现Runnable接口 通过Callable和Future创建线程 基于线程池的方式 java创建线程池的四种方式: ne ...
- 获取IP地址避免XFF攻击(使用nginx)
//以下代码可以获取到客户端IP,但是可能会有XFF攻击,伪造IP地址 request.getHeader("x-forwarded-for"); 解决办法 //从Nginx中X- ...