JavaScript 异步解决方案 Promise 全解析(转载)
Promise 是什么?
Promise
是一个 JS 的异步编程解决方案,解决了传统异步编程回调地狱的问题。 从语义上来说:Promise
是一个向外部传达异步编程操作消息的对象。
Promise 的三种状态
JS里一个promise可以有以下几种基本状态:
- nothing happened yet
- "locked in" to another promise
- fulfilled
- rejected
其中{1,2}为pending,{3,4}为settled,{2,3,4}为resolved,{1}为unresolved。
Promise
对象表示一个异步操作,拥有三种状态:
pending
(进行中)fulfilled
(已完成)rejected
(已失败)
只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 其中 pending
为初始状态,状态的变化只有两种:
pending
->fulfilled
(异步任务完成)pending
->rejected
(异步任务失败)
且一旦状态改变,状态就会凝固,不会再变化了。这就导致 Promise 一旦建立就会立即执行,无法取消。
Promise 的基本用法
ES6 规定 Promise 是一个构造函数,用来生成 Promise
对象实例。
const promise = new Promise(function(resolve,reject){
// 异步操作
if(success){ //异步操作成功
resolve(value);
} else{ //异步操作失败
reject(err);
}
})
复制代码
Promise
构造函数接收的参数是一个函数,该函数有两个由 JS 引擎指定的参数:resolve
函数 和 reject
函数
resolve
函数 有两个作用:- 将 Promise 状态由
pending
->fulfilled
(等待态 -> 成功态) - 将异步操作成功的结果 value 作为参数传递出去。(由后面讲的的 then 方法接收)
- 将 Promise 状态由
reject
函数 也有两个作用:- 将 Promise 状态由
pending
->rejected
(等待态 -> 失败态) - 将异步操作失败的错误信息
err
作为参数传递出去。(由后面讲的的then
/catch
方法接收)
- 将 Promise 状态由
Promise 的三个实例方法 then catch finally
1. Promise.prototype.then()
Promise
实例具有 then
方法,也就是说 then
方法时定义在原型对象上的。 Promise
实例生成后,可以用 then
方法分别指定 resolve
状态和 rejected
状态的回调函数:获取 Promise
内部的异步操作状态。
promise.then(
function (value) {
console.log(value); //异步操作成功时(fulfilled 态)调用
},
function (err) {
console.log(err); //异步操作失败时(rejected 态)调用
}
);
复制代码
then
方法可以接收两个回调函数作为参数:
第一个回调函数在 promise
实例变为 fulfilled
时调用,并获取 resolve
函数传递的参数 value。
第二个回调函数在 promise
实例变为 rejected
时调用,并获取 reject
函数传递的参数 err。
then 方法的返回值是一个新的 Promise
对象,因此可以 .then
可以链式调用。
2. Promise.prototype.catch()
Promise
实例的 catch
方法用于指定发生错误时的回调函数,是 .then(null, rejection)
的语法糖。
promise
.then(function (val) {
console.log(val); //异步操作成功时(fulfilled 态)调用
})
.catch(function (err) {
console.log(err); //异步操作失败时(rejected 态)调用
});
复制代码
上面代码中, 如果 promise
对象状态变为 fulfilled
,则会调用 then
方法指定的回调函数;如果异步操作抛出错误,状态就会变为 rejected
,就会调用 catch
方法指定的回调函数。另外,then
方法指定的回调函数,如果运行抛出错误,也会被 catch
方法捕获。
promise
.then((val) => console.log("fulfilled:", val))
.catch((err) => console.log("rejected:", err));
// 等价于
promise
.then((val) => console.log("fulfilled:", val))
.then(null, (err) => console.log("rejected:", err));
复制代码
如果 Promise
状态已经变成 resolved
,再抛出错误是无效的。因为 Promise
的状态一旦改变,就永久保持该状态,不会再变了。 Promise
对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止,也就是说错误总会被下一个 catch
语句捕获。
3. Promise.prototype.finally()
Promise
实例的 finally
方法用于指定不管状态最终如何,都会执行的函数。是 .then(function,function)
(function
相同)的语法糖。
promise.finally((message) => console.log("状态变化了", message));
// 等价于
promise.then(
(message) => console.log("状态变化了", message),
(message) => console.log("状态变化了", message)
);
// 无论成功还是失败都会执行
复制代码
Promise 的两个静态方法 all 和 race
1. Promise.all()
Promise.all(arr)
方法是挂载在 Promise
构造函数上的静态方法,它传入参数为一个 Promise
对象数组 arr
,返回值为一个 Promise
实例:
该实例会在 Promise
对象数组 内所有对象的状态变为 fulfilled
时调用内部的 resolve
函数; 该实例在 Promise
对象数组 内任意对象的状态变为 rejected
时调用 reject
函数(reject
函数的参数为第一个错误的 promise
对象的 err
); 有点类似于 JS
里的与操作(&&)
:所有表达式为真时返回真,任意表达式为假时返回假。
let p1 = new Promise((resolve,reject)=>{
resolve('p1-success'),
})
let p2 = new Promise((resolve,reject)=>{
resolve('p2-success'),
})
let p3 = new Promise((resolve,reject)=>{
reject('p1-error'),
})
Promise.all([p1,p2,p3]).then(val=>{
console.log(val)
}).catch(err=>{
console.log(err)
})
//输出 p1-error
复制代码
需要特别注意的是,Promise.all()
获得的成功结果的数组里面的数据顺序和 Promise.all()
接收到的数组顺序是一致的,即 p1
的结果在前,即便 p1
的结果获取的比 p2
要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用 Promise.all()
毫无疑问可以解决这个问题。
2. Promise.race()
Promise.race(arr)
方法返回一个 promise
实例,一旦 arr
中的某个 promise
对象解决或拒绝,返回的 promise
就会解决或拒绝。 顾名思义,Promise.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])
里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1-success");
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2-success");
}, 500);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p3-error");
}, 1000);
});
Promise.race([p1, p2, p3])
.then((val) => {
console.log(val);
})
.catch((err) => {
console.log(err);
});
//输出 p2-success
作者:Prayx
链接:https://juejin.cn/post/6862929170390450183
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话