Promise 是JavaScript异步编程的一种解决方案。
异步编程
同步与异步
在JavaScript中,许多任务的运行的模式是同步(synchronous)的,代码按照顺序一行一行执行,执行完一个任务再执行下一个。
但是,有些任务不适合这种运行模式:
发起一个网络请求,得到响应后对返回数据进行一些处理
加载一个script文件,文件加载完成之后调用script文件中的一些方法
由于这些任务的执行耗时可能较长,导致排在它后面的任务需要等待它完成之后才能执行,这就形成了阻塞(blocking)。
一些语言对此类需要耗时的任务的解决方式是多线程。而JavaScript是单线程执行的,它对此类任务的解决方式是异步(asynchronous)。
JS异步操作的几种方式
回调函数
事件监听
发布/订阅模式
Promise
Promise的意义
我们使用一个通过ajax
发送请求的例子:
$.ajax({ |
处理data
的逻辑,需要放在success
函数体内,否则这段代码是访问不到data
的。这样会导致处理数据的代码与发送请求代码耦合 ,这又会带来另一个问题:如果再发起一个请求,需要用到data
,这个请求代码需要写在success
内,多层嵌套会出现回调地狱。
使用Promise的话,上面的代码可改写为:
const p = new Promise((resolve, reject) => { |
Promise不关心请求成功之后对数据的处理逻辑,它只负责用resolve
或者reject
来改变Promise状态。
promise
直译为承诺,我们可以这样理解:
const p = new Promise((resolve, reject) => { |
使用Promise
创建Promise
let p = new Promise(function(resolve, reject) { |
Promise构造方法接收一个函数参数,叫** 处理器函数** (executor function),处理器函数接收两个函数参数:resolve
和reject
。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
注意,处理器函数中的代码是立即执行的:
new Promise((resolve, reject) => { |
处理器函数中可以放那些耗时长的任务,然后等任务完成再使用resolve
或reject
改变状态。
Promise的状态
Promise对象通过自身的状态来控制异步操作。Promise实例具有三种状态:
异步操作未完成(
pending
)异步操作成功(
fullfilled
)异步操作失败(
rejected
)
这三种状态的变化途径有两种:
从未完成变成成功,Promise实例会传回一个值,状态变为
fullfilled
。从未完成变成失败,Promise实例会抛出一个错误,状态变为
rejected
。
注意,Promise的状态由pending
变为fullfilled / rejected
二者之一后,就已敲定了,不能再改变。
new Promise((resolve, reject) => { |
Promise实例的then
方法,用来添加回调函数。then方法接收两个参数,第一个是异步操作成功时的回调函数,第二个是异步操作失败时的回调函数(可以省略)。
// 异步操作成功时(fullfilled),会执行f1 |
链式操作
then
函数返回一个新的Promise实例,因此,可以链式操作Promise:
ajax(url) |
这解决了回调地狱的问题。
then返回值的处理技巧
let p1 = new Promise((resolve, reject) => { |
p2
是p1.then()
返回的新的Promise对象,那么p2
的状态是什么呢?
如果
p1
的回调返回一个字符串,那么p2
的状态是成功;如果
p1
的回调返回一个Promise对象,那么p2
的状态就是返回的这个Promise对象的状态。
Promise.all
Promise.all()
方法用于将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.all([p1, p2, p3]) |
p
的状态由p1
、p2
、p3
的状态共同决定,具体规则是:
只有
p1
、p2
、p3
的状态都为fullfilled
,p的状态才为fullfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传给p
的回调函数;只要
p2
、p2
、p3
之中有一个被rejected
,p的状态就变为rejected
,此时第一个被reject
的实例的返回值,会传给p的回调函数。
Promise.race
Promise.race()
和all()
类似,同样是将多个Promise实例包装成一个新的Promise实例。
const p = Promise.race([p1, p2, p3]) |
p
的状态变化规则:
只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就会跟着改变。那个率先改变的Promise实例的返回值,会传给p
的回调函数。
错误处理
使用promise.prototype.catch()捕获异常。如果Promise对象的then
方法中没有传失败状态执行的回调函数,那么可以在.catch()
中来捕获失败时抛出的异常。
promise.prototype.finally()则是无论成功或失败都会执行的回调函数。
async和await
async
和await
是Promise的语法糖。
给一个函数加上async
关键字,这个函数返回的就是一个Promise对象。
async function test() { |
await
就是将函数返回值使用 Promise.resolve()
包裹了下,和 then
中处理返回值一样,并且 await
只能配套 async
使用。
async function test() { |
async
和await
能更一步优化回调地狱。Promise是把层层嵌套的回调“展开”成扁平的链式调用,async / await
把链式调用都省了,让我们可以像写同步代码那样写异步代码。
async getLessons(username) { |
参考
Author: kpt
Permalink: http://kpt.ink/2021/10/03/Promise-Basic/
文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。