写法说明:
Promise表示类
promise表示实例对象

基本用法

  1. const result = await promise //xxx值 = 一个异步任务

简易例子

Promise的封装

  1. function 摇色子(){
  2. return new Promise((resolve,reject)=>{
  3. setTimeout(()=>{
  4. resolve(Math.floor(Math.random()*6)+1)
  5. },3000)
  6. })
  7. }
  1. 必须return 一个new Promise
  2. new Promise接受一个函数,这个函数就是你要做的事情
  3. 函数要包含 resolve 成功后调用,reject 失败后调用

使用Promise

  1. 摇色子().then(s1,f1).then(s2,f2)

用控制台试一下


image.png
摇色子() 执行这个函数,会返回一个Promise


image.png
使用成功回调 s1。 在两秒钟之后打印出成功

  1. 获取摇色子结果

image.png
使用resolve方法的参数获取

Promise回调的执行时机

改写一下上面的代码,不再使用计时器了

  1. function 摇色子(){
  2. return new Promise((resolve,reject)=>{
  3. resolve(Math.floor(Math.random()*6)+1)
  4. })
  5. }

再执行 一次then看看
image.png
甚至比Promise出来的还快一点

那么在之后加一个console.log呢
image.png
先打印出end,再出结果
很明显的结果,因为Promise是一个异步任务,所以之后的代码执行完再打印出结果是很容易想到的

那么如果之后的代码里面有其他的异步任务呢
image.png
先出promise结果,再出timeout

这就涉及到了常说的微任务和宏任务了:
由于本来是没有Promise的,ES6之后才有的。所以之前的js很单纯,就只有两个东西:当前任务,延时任务。
image.png
首先主线程会去执行所有的同步任务。等到同步任务完成之后,就会去看任务队列里面的异步任务。如果异步任务满足条件,那么异步任务就会重新进入主线程开始执行,这时候他就会变成同步任务。 就这么循环重复。

那ES6之后呢:
为了让Promise回调更早地执行,强行插入了一个队列
就把上面的异步队列,分成了两个:一个是微任务,一个是宏任务。就是两个队列
setimeout这些呢 放入宏任务的队列
ajax.then(),promise这些 放入微任务的队列

一般就先执行微任务里面的任务,执行完之后再去找宏任务里面的。
如果宏任务里面有微任务,就里面的微任务放进队列里,做完宏任务再去微任务,一直重复
image.png
这个就很清楚了。
先做完摇色子1(微任务)
再去做settimeout(宏任务),发现有摇色子2(微任务)。
做完settimeout再去摇色子2。

就是名字取得不好,理解成小任务,大任务,然后先做小任务就好了。

Promise的其他API

Promise.resolve(result)

制造一个成功(或失败)
有的时候,我们需要将所有东西都变成promise
比如说 上面的摇色子,我想造假,每一次都摇出1
如果像之前写的,就很麻烦:

  1. function 摇色子(){
  2. const result = 1
  3. return new Promise((resolve,reject)=>{
  4. resolve(result)
  5. })
  6. }
  7. 摇色子().then(n=>console.log(n))
  8. // 1

用Promise.resolve就很快

  1. function 摇色子(){
  2. return Promise.resolve(1)
  3. }
  4. 摇色子().then(n => console.log(n))
  5. //1
  6. 摇色子().then(n => console.log(n))
  7. //1
  8. 摇色子().then(n => console.log(n))
  9. //1

怎么摇都是1,玩晒。
resolve不总接受一个成功,有可能是失败
image.png
如果是失败,那么resolve也制造一个失败的promise。

Promise.reject(reason)

制造一个失败
跟resolve差不多,但是他是一定会失败的。
image.png

Promise.all(数组)

等待全部成功,或者有一个失败
跟数组的every是差不多的
image.png
如果数组里面的全部成功了,就把结果放进一个数组里面

那么失败了呢
image.png
它不会管哪个成功了,只要有一个失败,就返回失败。
如果有一个失败的话,后面的失败 他也不管的
所以要获取后面的失败怎么办呢,所以就有下面这个api

Promise.allSettled(数组)

把上面的代码改一改
image.png
它会返回一个数组给你,包含对应的promise的状态和返回值
而且它很难失败,所以用成功的回调就可以处理了。

所以就可以用来同时发很多请求,又能把所有结果拿到手。
这个是新的api,很多浏览器不支持
image.png
由于这个api的兼容性实在是太差了,很难用在项目中,但是这个api又非常有用,所以我们可以用promise.all伪造一个promise.allSettled

promise.all伪造promise.allSettled

先明白promise.all的特性,他跟allsettled的区别:
主要在于promise.all一旦遇到失败,就不管其他的promise了,所以有失败的情况是拿不到所有的数据的。
那么解决方法就是:把promise.all里面的promise,全部都搞成一定成功
promise().then()是一定返回成功的,所以用then就可以了

  1. let task1 = ()=>new Promise((resolve,reject)=>{
  2. setTimeout(()=>{reject(`task1失败了`)},3000)
  3. })
  4. let task2 = ()=>new Promise((resolve,reject)=>{
  5. setTimeout(()=>{resolve(`task2成功了`)},4000)
  6. })
  7. let task3 = ()=>new Promise((resolve,reject)=>{
  8. setTimeout(()=>{reject(`task3失败了`)},5000)
  9. })
  10. Promise.all([
  11. task1().then(()=>({status:'ok'}),()=>({status:'not ok'})),
  12. task2().then(()=>({status:'ok'}),()=>({status:'not ok'})),
  13. task3().then(()=>({status:'ok'}),()=>({status:'not ok'}))
  14. ]).then(
  15. result => console.log(result)
  16. )

image.png
比如task1,当他失败之后,task1.then()会返回一个成功的对象 叫{status:’not ok’}
这样他就永远不会失败了(then)
如果觉得很麻烦,我们可以写一个promise的转换函数

  1. const transPromise = (promise)=>promise.then(
  2. (val)=>({status:'ok',val}),
  3. (reason)=>({status:'not ok',reason})
  4. )
  5. Promise.all([
  6. transPromise(task1()),
  7. transPromise(task2()),
  8. transPromise(task3())
  9. ]).then(
  10. result => console.log(result)
  11. )

image.png
还是能继续优化的:
比如tansPromise是接收一个promise list去处理的。 就更简洁了

  1. const transPromise = (promiseList)=>promiseList.map(
  2. (promise)=>promise.then(
  3. (val)=>({status:'ok',val}),
  4. (reason)=>({status:'not ok',reason})
  5. )
  6. )
  7. Promise.all(transPromise([task1(),task2(),task3()])).then(v=>console.log(v))

image.png

甚至能直接整一个promise.allSettled

  1. Promise.allSettled2 = function(promiseList){
  2. return Promise.all(transPromise(promiseList))
  3. }

image.png
就跟原版的allSettled一样了。

Promise.race(数组)

发很多个请求,只要有一个请求成功了,就成功了

比如说要备份用户数据,但是不知道备份到哪个服务器,比如说一个中国服务器,一个美国服务器。
所以就同时向中国和美国服务器发请求备份,哪个先成功了就用哪个,另外一个就不要了