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 协议进行许可,使用时请注意遵守协议。