对于实例方法then()
和catch()
,需要清楚的是,会返回一个新的Promise
对象
同一个Promise
对象,可以绑定多个回调,但链式使用then()
时,并不是一直操作一个对象,而是不断返回的新对象
const p = new Promise((resolve,reject) => {
resolve(10)
})
p.then(value => {
console.log("一号回调")
console.log(value)// 10
return 20
})
p.then(value => {
console.log("二号回调")
console.log(value)// 10
return 20
}).then(value => {
console.log(value)// 20
})
执行顺序是正常的从上至下,对一个对象绑定多个回调的情况很少,通常以链式调用为主
当then()
返回的是非Promise
对象时,将此返回值当作返回的Promise
对象的结果,状态为fulilled
;如果返回了一个Promise
对象,那么它就是返回值
catch()
同样遵循此规则,也就是说,catch()
后续可以继续接着then()
,源源不断…
函数执行过程中出现错误,同样按照失败来处理
如果细看then()
与catch()
,这和try
与catch
没有多大差别,差一个常被遗忘的成员finally
,Promise
也提供了这个实例方法,就是使用的非常少,此处便不再细说
静态方法
实例对象的方法和属性都是属于短小精悍的存在,除此之外,Promise
还有一些静态方法
all()
- 接收一个数组,返回
Promise
对象。根据数组成员的状态,决定Promise
状态与结果 - 对于数组成员
- 非
Promise
对象,则转换为成功状态Promise
对象,当前成员作为成功结果 Promise
对象,以其自身表现为主
- 非
- 当所有成员进入了成功状态,那么返回对象进入成功状态,将所有成员的成功结果包裹为数组,作为自己的成功结果
- 如果有一个成员失败了,那么返回对象进入失败状态,其失败原因,为第一个失败的成员的失败原因,其他成员不会停止执行
Promise.all([
new Promise((resolve) => {
resolve(10);
}),
new Promise((resolve) => {
resolve(20);
}),
]).then((values) => {
console.log(values); // [10, 20]
});
allSettled()
- 与
all()
不同于,如果出现失败的成员,不会导致返回对象的失败,而是等到所有成员都执行完毕之后,再返回 - 因为可能出现失败的
Promise
对象,此时返回的结果会多一层包装。与all()
相比,不追求全部成功,而是结果class Result { //状态 status: "fulfilled" | "rejected" //成功的结果 value?: any //失败的原因 reason?: any }
race()
- 参数形式与
all()
一致,不同的是,只关注第一个完成的成员 - 第一个完成成员,将作为此方法的返回值,如同比赛一般
Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => resolve(10), 100)
}),
new Promise((resolve, reject) => {
setTimeout(() => resolve(20), 200)
})
]).then((value) => {
console.log(value) // 10
})
any()
- 与
race()
相仿,只关注第一个完成的成员,但关注的是成功,而非失败 - 当第一个成员结束是成功时,表现与
race()
一致;只有当所有的成员都进入了失败状态,返回对象才进入失败状态,并且不会提供成员的失败原因 - 更适合获取一组请求当中,第一个成功的结果,当然,也丢失了所有的失败原因
Promise.any([
new Promise((resolve, reject) => {
setTimeout(() => reject(10), 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => resolve(20), 2000)
})
]).then((value) => {
console.log(value) // 20
})
resolve()
& reject()
- 快速生成对应的
Promise()
实例
Promise.resolve(10).then((value) => {
console.log(value) // 10
})
Promise.reject("失败了").catch((err) => {
console.log(err) //失败了
})
async与await
还有两个特殊的关键词,async
与await
,很有趣的两个小玩意
async
async
,直译就是异步,使用在函数上,如同class
、public
一般,给一个函数打上异步记号
//函数声明,需要加在function的前面
async function asyncFn1() {
return 10
}
//函数表达式同理,此处是箭头函数省略function罢了
const asyncFn2 = async () => {
return 20
}
当一个函数被打上了async
记号之后,它便不是它自己了吗?当然不是,此处会产生的变化是在返回值上,让函数表现的如同then()
一样,将返回值包装成一个Promise
对象。恩对,就这点
async
再说await
,它就不能单独使用了,只能在标记了async
的函数当中去使用
遵循这一条之后,再说表现,如同直译,等待,等谁下课呢,一个对象…
(async () => {
const result = await new Promise((resolve => {
setTimeout(() => {
console.log("发回响应")
resolve("成功结果")
}, 2000)
}))
console.log(result)// 2s之后输出 "成功的结果"
})()
需要注意的是,输出的顺序可是从上至下,如同同步顺序一般执行,这就是await
,等待右侧Promise
做出改变之后,将结果交给左边
若是执行过程中失败或者出现错误,那么async
就会进入失败状态,并且跳出执行,如同手动抛出了错误,可以由catch
捕获
(async () => {
try {
const result1 = await new Promise(((resolve, reject) => {
console.log("开始执行一号任务")
setTimeout(() => {
reject("失败啦")
}, 2000)
}))
const result2 = await new Promise(((resolve, reject) => {
console.log("开始执行二号任务")
setTimeout(() => {
reject("失败啦")
}, 2000)
}))
console.log(result)
} catch (e) {
console.log(e)
}
})()
结合使用
在两个标记的加持下,便获得神奇的能力,伪同步执行代码
可以以同步思维来写异步代码,由await
等待结果,不必想什么回调、异步结果时机、错误,都能统一处理,其流程也是符合正常的阅读习惯,从上至下
这都是属于是
Promise
的手段,async
和await
只是语法糖
现在再回看之前写的读取文件示例,可以这样改
const fs = require("fs/promises")
const readFile = async () => {
const data1 = await fs.readFile("./doc/1.log", "utf-8")
const data2 = await fs.readFile("./doc/2.log", "utf-8")
const data3 = await fs.readFile("./doc/3.log", "utf-8")
return [data1, data2, data3]
}
readFile().then((value) => {
console.log(value)
}).catch((err) => {
console.log(err)
console.log("外层捕获到错误")
})
很重要的一点是,async
函数的返回值是Promise
对象,这一点是不变的,部分框架、插件提供hook函数
并不支持此类返回值,如vue3
的setup()
、React
的useEffect()
最后
没有Promise
,很多事情一样能做,而有了Promise
,能让过程、结果、错误处理,在总体上条理清晰