一、为什么使用Promise?
我们知道 js 执行的时候,一次只能执行一个任务,它会阻塞其他任务。由于这个缺陷导致 js 的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以使用回调函数执行。
常见的异步模式有以下几种:
1 2 3 4 5 6 7
| function callBack(){ console.log('执行完成') } console.log('before setTimeout') setTimeout(callBack,1000) console.log('after setTimeout')
|
控制台输出:
1 2 3
| before setTimeout after setTimeout 执行完成 //1秒后打印
|
上述定时器是在固定时间触发某个回调函数。
对于 ajax 网络请求就没有这么简单了,可能有多个网络请求是关联的,先执行某个请求返回结果后,第一个返回结果作为第二个请求的参数,调用第二个网络请求。如此,如果业务复杂,网络请求太多时,回调也很多,容易出现回调地狱。所以 Promise 出现了,专门解决异步回调地狱问题。
通俗地讲,Promise 就像一个容器,里面存放着未来才会结束,返回结果的容器,返回的结果只需要在出口处接收就好了。从语法上讲,Promise 是一个对象,从它可以获取异步操作的消息。
二、Promise基本使用
Promise 实例化的时候,传入的参数是一个函数,函数中接收两个参数:
1 2 3 4 5 6 7
| const p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('123') },1000) }).then(res=>{ console.log(res) })
|
传入的 resolve 和 reject 本身都是函数。其作用分别为:
resolve - 把 Promise 的状态从进行中变为成功状态。
reject - 把 Promise 的状态从进行中变为拒绝状态。
Promise的三种状态:
pending :进行中,表示 Promise 还在执行阶段,没有执行完成。
fulfilled:成功状态,表示 Promise 成功执行完成。
rejected:拒绝状态,表示 Promise 执行被拒绝,也就是失败。
Promise 的状态,只可能是其中一种状态,从进行中变为成功或失败状态之后,状态就固定了,不会再发生改变。
Promise.then
执行 resolve 时,Promise 状态变为 fulfilled ,会执行 .then 方法。then 方法接收的参数也是一个函数,函数中携带一个参数,该参数是 resolve(res) 返回的数据。
Promise.catch
执行 reject 时,Promise 状态从 pending 变为 rejected,会执行 catch 方法,catch 方法接收的也是一个函数,函数中携带一个参数,该参数为 reject(err) 返回的数据。
1 2 3 4 5 6 7 8 9
| const p = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('error message') },1000) }).then(res=>{ console.log(res) }).catch(err=>{ console.log('err',err) })
|
三、Promise 链式调用
制作一个模拟网络请求:
- 第一次返回 a,
- 修改返回的结果为 aa,作为第二次网络请求返回的结果。
- 修改结果为 aaa,作为第三次返回结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const pp = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('a') },1000) }).then(res=>{ console.log('res1',res) return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(res+'a') },1000) }) }).then(res=>{ console.log('res',res) return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(res+'a') },1000) }) }).then(res=>{ console.log('res3',res) })
|
这种场景其实就是接口的多层嵌套使用,Promise 可以把多层嵌套按照线性的方式进行书写,非常优雅。我们把 Promise 的多层嵌套调用就叫做链式调用。
上述实例,有三层嵌套就 new 了 3 个Promise,代码写得比较多,我们看看在实现功能的前提下如何能够简化。
四、Promise 嵌套使用的简写
promise传入的函数参数reject是一个非必传的参数,如果不需要处理失败时的结果时,我们可以省略掉 reject 。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const ppp = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('a') },1000) }).then(res=>{ console.log('res1',res) return new Promise(resolve=>resolve(res+'a')) }).then(res=>{ console.log('res',res) return new Promise(resolve=>resolve(res+'a')) }).then(res=>{ console.log('res3',res) })
|
Promise 嵌套使用时,内层的 Promise 可以省略不写,所以我们可以直接把 Promise 相关的去掉,只返回最后一个结果返回,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| const pppp = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('a') },1000) }).then(res=>{ return res+'a' }).then(res=>{ return res+'a' }).then(res=>{ console.log('res3',res) })
|
同理,失败状态catch也能简写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const ppppp = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('a') },1000) }).catch(err=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ reject(err+'a') },1000) }) }).catch(err=>{ console.log('err',err) })
const pppppp = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('a') },1000) }).catch(err=>{ return new Promise((resolve,reject)=>reject(err+'a')) }).catch(err=>{ console.log('err',err) })
const ppppppp = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('a') },1000) }).catch(err=>{ throw err+'a' }).catch(err=>{ console.log('err',err) })
|
五、Promise方法
all 方法
Promise.all 方法,提供了并行执行异步操作的能力,并且在所有异步操作完成之后,统一返回所有结果。具体使用如:
1 2 3 4 5 6
| Promise.all([ new Promise(resolve=>resolve('a')), new Promise(resolve=>resolve('b')), ]).then(res=>{ console.log('all',res) })
|
控制台输出一个all数组,数组长度取决于 Promise 的个数。
一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各类资源,所有的都加载完后,再进行页面的初始化。
race方法
race翻译成中文:赛跑。就是谁跑得最快,谁才能触碰到终点的胜利线。
Promise.race 用法与 all 一样,只是返回结果上不同,它返回的是执行最快的那个 Promise 的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Promise.race([ new Promise(resolve=> setTimeout(()=>{ resolve('a') },100) ), new Promise(resolve=> setTimeout(()=>{ resolve('a') },200) ), ]).then(res=>{ console.log('race',res) })
|