zoukankan      html  css  js  c++  java
  • promise详解

    1、promise

    1.1、为什么用promise

    主要为了解决回调地狱的问题

    异步结构不清晰,promise可以让异步操作结构变得很清晰

    1.2、promise语法

    executor是带有 resolvereject 两个参数的函数 。Promise构造函数执行时立即调用executor 函数, resolvereject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolvereject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。

    简单理解上面一段话,new Promise()里面接收一个函数,这个函数会理解执行,函数里面有两个参数resolve和reject,函数的执行体里面是异步的操作,异步操作有成功有失败

    • 成功:状态pending->fulfilled,并且用resolve 去接收成功的值
    • 失败:状态pending->rejected,并且用reject 去接收失败的值
    • 3种状态两种结果
    new Promise( function(resolve, reject) {...} /* executor */  );
    

    简单例子

     let p1=new Promise(function(resolve,reject){
               setTimeout(function(){
                   let num=new Date().getTime();
                   num%2==0?resolve('成功'):resolve('失败')
               },0)
            })
            p1.then(function(value){
                console.log(value)
            },function(reason){
                console.log(reason)
            })
    

    连写

     let p1=new Promise(function(resolve,reject){
               setTimeout(function(){
                   let num=new Date().getTime();
                   num%2==0?resolve('成功'):resolve('失败')
               },0)
            }).then(function(value){
                console.log(value)
            },function(reason){
                console.log(reason)
            })
    

    如果一个promise即调用了resolve,又调用了reject,谁先调用,最后就走对应的方法,

     new Promise(function (resolve, reject) {
                resolve("成功")
                reject("失败")
                console.log("执行了")
            }).then(value => {
                console.log(value)
            }, reason => {
                console.log(reason)
            })
            // 执行了
            // 成功
    

    如果没有成功,刚在then的第二个参数写的失败的回调函数,其实也可以用catch

     new Promise(function (resolve, reject) {
               reject("失败")
                resolve("成功")
            })
            .then(value => {
                console.log(value)
            })
            .catch(reason => {
                console.log(reason)
            })
            // 失败
    

    1.3、Promise.resolve()

    成功的语法糖

    let p1=new Promise(function(resolve,reject){
                resolve(11)
            })
            p1.then(function(value){
                console.log(value)
            })
    

    Promise.resolve()

    const p1=Promise.resolve(11); //跟上面是一样的
    p2.then(value=>{console.log(value)})
    

    1.4、Promise.reject()

    失败的语法糖

    const p3=Promise.reject(33)
    p2.then(null,reason=>{console.log(reason)})
    

    1.5、Promise.All()

    Promise.All():发送了多个请求,只有全部成功才走成功的处理,只要其中有一个失败就失败,这个返回的是p2的原因

    • 失败原因,走第一个失败的原因
     const p1 = Promise.resolve(11); //跟上面是一样的
            const p2 = Promise.reject(22)
            const p3 = Promise.reject(33)
        
            const pAll = Promise.all([p1, p2, p3])
            pAll.then(
                value => {},
                reason => {
                    console.log(reason)
                }
            )
    
    • 成功是几个结果组成的数组,结果顺序跟[p1, p2, p3]一样
     const p1 = Promise.resolve(11); //跟上面是一样的
            const p2 = Promise.resolve(22)
            const p3 = Promise.resolve(33)
        
            const pAll = Promise.all([p1, p2, p3])
            pAll.then(
                values => {
                    console.log(values)
                }
            )
    //[11,22,33]
    

    1.6、Promise.race()

    多个异步任务,谁先执行完就用谁的,可以用setTimeout延迟去模拟,这里我就不试了

    const p1 = Promise.resolve(11); //跟上面是一样的
            const p2 = Promise.resolve(22)
            const p3 = Promise.resolve(33)
        
            const pRace = Promise.race([p1, p2, p3])
            pRace.then(
                value => {
                    console.log(value)
                }
            )
    

    如果第一个执行完是一个失败的,那就走失败

     const p1 = Promise.reject(11); //跟上面是一样的
            const p2 = Promise.resolve(22)
            const p3 = Promise.resolve(33)
        
            const pRace = Promise.race([ p1,p2, p3])
            pRace.then(
                value => {
                    console.log(value)
                },
                reason=>{
                    console.log(reason)
                }
            )
    

    1.7、promise理解

    1.7.1、如何改变promise的状态?
        resolve(value): 如果当前是pendding就会变为resolved
        reject(reason): 如果当前是pendding就会变为rejected
        抛出异常: 如果当前是pendding就会变为rejected
    
    //如果当前是pendding就会变为rejected,内部抛出也是这样
    const p=new Promise((resolve,reject)=>{
      throw new Error('出错了')
    })
    
    const p = new Promise((resolve, reject) =>{
            //resolve(1)//promies变为resolved成功状态
            //reject(2)//promise变为rejected失败状态
            //throw new Error("出错了")//抛出异常promise变为rejected失败状态,reason 为抛出的error
            throw 3
        });
        p.then(
            reason => {console.log("reason:", reason)}//3
        )
    
    1.7.2、一个promise指定多个成功/失败回调函数,都会调用吗?
    	都会调用
    
     const p1=Promise.resolve('11')
            p1.then(value=>{
                console.log("第一次"+value)
            })
            p1.then(value=>{
                console.log("第二次"+value)
            })
            //第一次11
            //第二次11
    
    1.7.3、改变promise状态和指定回调函数谁先谁后
       3.1、都有可能,正常情况下时先指定回调函数再改变状态,但也可以先改变状态再指定回调函数
       3.2、如何先改变状态再指定回调?
     	 3.2.1、在执行器中直接调用resolve()/reject()
         3.2.2、延迟更长时间才调用then()
    
    new Promise((resolve, reject) =>{
            setTimeout(()=>{
                resolve(1)//后改变的状态(同时指定数据),异步执行回调函数
            }, 1000)    
        }).then(//先指定回调函数,保存当前指定的回调函数
            value => {console.log("value1:", value)}//value1: 1
        )
    
    1.7.4、promise.then()返回的新的promise的结果状态由什么决定?

    如果没有返回值也没有抛出错误,就走成功undefined

      4.1、简单表达:由then()指定的回调函数执行的结果决定
      4.2、详细表达:
        4.2.1、如果抛出异常,新promise变为rejected,reason为抛出的异常
        4.2.2、如果返回的是非promise的任意值,新的promise变为resolved,value为返回的值
        4.2.3、如果返回的是另一个新promise,此promise的结果就会成为新promise的结果 
    
    new Promise((resolve,reject)=>{
                setTimeout(function(){
                    resolve(11)
                },1000)
            })
            .then(value=>{
                console.log("第一次"+value)
            })
            .then(value=>{
                console.log("第二次"+value)
            })
            //第一次11
            //第二次undefined
    
     new Promise((resolve,reject)=>{
                setTimeout(function(){
                    reject(11)
                },1000)
            })
            .then(value=>{
                console.log("成功第一次"+value)
            },reason=>{
             console.log("失败第一次"+reason)
            })
            .then(value=>{
                console.log("成功第二次"+value)
            },reason=>{
             console.log("失败第二次"+reason)
            })
            //失败第一次11
            // 成功第二次undefined
    

    以下都是针对第二次then的结果

    new Promise((resolve,reject)=>{
                setTimeout(function(){
                    resolve(11)
                },1000)
            })
            .then(value=>{
                console.log("成功第一次"+value)
                // return 2  //成功第二次2
                // return Promise.resolve(2)  //成功第二次2
                // return Promise.reject(2)  //失败第二次2
                throw 3;  //失败第二次3
    
    
    
            },reason=>{
             console.log("失败第一次"+reason)
            })
            .then(value=>{
                console.log("成功第二次"+value)
            },reason=>{
             console.log("失败第二次"+reason)
            })
    
    1.7.5、 promise如何串连多个操作任务
    1、promise的then()返回一个新的promise,可以看成then()的链式调用
    2、通过then的链式调用串连多个同步/异步任务
    
    new Promise((resolve, reject) =>{
            setTimeout(()=>{
                console.log("执行任务1(异步)")
                resolve(1)
            },1000);
        }).then(
            value => {
                console.log("任务1的结果:", value)
                console.log("执行任务2(同步):")
                return 2
            }
        ).then(
            value => {
                console.log("任务2的结果():", value)
                return new Promise((resolve, reject)=>{
                    setTimeout(()=>{
                        console.log("执行任务3(异步)")
                        resolve(3)
                    },1000)
                })
            }
        ).then(
            value => {
                console.log("任务3的结果", value)
            }
        )
    /*
    执行任务1(异步)
    任务1的结果: 1
    执行任务2(同步):
    任务2的结果(): 2
    执行任务3(异步)
    任务3的结果 3
    */
    
    1.7.6、promise异常穿透
    1、当使用promise的then链式调用时,可以在最后指定失败的回调
    2、前面任何操作出了异常,都会传到最后失败的回调中处理
    

    第一个走的失败的回调,但是失败的回调没有写,默认他会执行 reason=>{throw reason},到第三个时,由于第二个没有抛出异常,也没有返回值,所以走成功值为undefined

    catch在后面也不会成功,因为第三个走的成功,所以不会执行catch

    new Promise((resolve, reject) => {
                reject(1)
            }).then(
                value => {
                    console.log("onResolveed1():", value)
                    return Promise.reject(2)
                }
            ).then(
                value => {
                    console.log("onResolveed2():", value)
                    return 3
                },
                reason => {
                    console.log("第二个失败"+reason)
                }
            ).then(
                value => {
                    console.log("onResolveed3()", value)
                }
            ).catch(err=>{
                console.log("catch"+err)
            })
    /*
      第二个失败1
      onResolveed3() undefined
    */
    

    默认在catch后面的then函数,执行成功和失败回调和上面的规则是一样的

     new Promise((resolve, reject) => {
                reject(1)
            }).catch(err => {
                console.log(err)
            }).then(value => {
                console.log("成功"+value)
            }, reason => {
               console.log("失败"+reason)
            })
    /*
    1
    成功undefined
    */
    

    如果我不想执行后面then的函数呢?这就看下一个中断promise链

    1.7.7、中断promise链?
    1、当时用promise的then的链式调用时,在中间中断,不再调用后面的回调函数
    2、办法:在回调函数中返回一个pedding状态的promise对象
    
     new Promise((resolve, reject) => {
                reject(1)
            }).catch(err => {
                console.log(err)
                return new Promise((resolve,rejuct)=>{}) //返回一个pending的promise
            }).then(value => {
                console.log("成功"+value)
            }, reason => {
               console.log("失败"+reason)
            })
    

    2、async和await

    2.1、async

    1、函数的返回值为promise对象
    2、promise对象的结果由async函数执行的返回值决定
    

    只要加了async,返回一个promise,里面保存了状态,如果async函数成功,下面就走成功回调,如果是失败,就失败的回调函数函数

    async function fn1() {
           return 1
    }
    let result=fn1();
    console.log(result)
    //Promise
    
    
    
    async  function fn1() {
          return 1
    }
    fn1().then(value=>{
      console.log(value)
    })
    
    //1
    

    2.2、await

    该指令会暂停异步函数的执行,并等待Promise执行,然后继续执行异步函数,并返回结果。

    1、await右侧的表达式一般为promise对象,但也可以是其他的值 
    2、如果表达式是promise对象,await返回的是promise成功的值
    3、如果表达式是其他值,直接将此值作为await的返回值
    

    如果value的右边是promise,返回的是promise成功时候的值

    如果value的右边是promise,返回的是promise失败时候的值,就用try catch来获取

    如果value右边的不是promise,返回的是值本身

     function fn2() {
            return  new Promise((resolve,reject)=>{
               setTimeout(()=>{
                   resolve(5)
               },10)
             })
        }
         async function fn1() {
              const value=await fn2();
              console.log(value)
        }
        fn1() //5
    

    注意:

     await必须写在async函数中,但是async函数中可以没有await

     如果await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理

       

    3、 JS异步之宏队列与微队列

    1、JS中用来存储执行回调函数的队列包含2个不同特定的队列
    2、宏队列:用来保存带执行的宏任务,比如:定时器回调,DOM事件回调,ajax回调
    3、微队列:用来保存待执行的微任务,比如:promise的回调,MutationObserver的回调
    4、JS执行时会区别这2两个队列
        1、JS引擎首先必须先执行所有的初始化同步任务代码
        2、每次准备取出第一个宏任务前,都要将所有的微任务一个一个取出来执行
    

    注意promise放在微任务里面,需要更改状态才会放到微任务里面,比如是从pending =》resolved

    或者 pedding变为rejected 才会放到微队列

     setTimeout(()=>{
           console.log('settimeout')
       },0)
       Promise.resolve(1).then(value=>{
           console.log("promise"+value)
       })
       
       /*
          promise1
          settimeout
       */
    

    js会把程序走一遍,定时器加到宏队列,promise加到微队列,

    每次会先把微队列执行完在执行宏队列

     setTimeout(()=>{
           console.log('定时器1')
       },0)
    
       setTimeout(()=>{
           console.log('定时器2')
       },0)
       Promise.resolve(1).then(value=>{
           console.log("第一个promise"+value)
       })
       Promise.resolve(2).then(value=>{
           console.log("第二个promise"+value)
       })
    
    /*
    第一个promise1
    第二个promise2
    定时器1
    定时器2
    */
    

    这里看下是先执行 第三个promise 还是先执行 定时器2

     setTimeout(()=>{
           console.log('定时器1')
           Promise.resolve(3).then(value=>{
           console.log("第三个promise"+value)
       })
       },0)
    
       setTimeout(()=>{
           console.log('定时器2')
    
       },0)
       Promise.resolve(1).then(value=>{
           console.log("第一个promise"+value)
       })
       Promise.resolve(2).then(value=>{
           console.log("第二个promise"+value)
       })
      /*
        第一个promise1
        第二个promise2
        定时器1
        第三个promise3
        定时器2
      */
    

    这个也很好理解,当执行第一个定时器时,就把promise添加到了微队列

    执行定时器2的时候,这个时候把微队列的取出来执行,所以第三个promise 先执行 定时器2后执行

    4、面试题

    第一题

     setTimeout(()=>{
           console.log(1)
       },0)
       new Promise((resolve)=>{
           console.log(2)
           resolve()
       }).then(()=>{
           console.log(3)
       }).then(()=>{
           console.log(4)
       })
    
       console.log(5)
    //25341
    

    第一个看同步执行 25

    当执行到第一个then时,上面已经有结果了 pedding变为resolved ,所以放到了微队列了

    这时候执行第一个then,执行完了第二个then就有状态变化了 pedding变为resolved,这里也放到了微队列

    最后执行定时器

    第二题

      const first = () => (new Promise((resolve, reject) => {
                console.log(3);
                let p = new Promise((resolve, reject) => {
                    console.log(7);
                    setTimeout(() => {
                        console.log(5);
                        resolve(6)
                    }, 0)
                    resolve(1)
                })
                resolve(2)
                p.then((arg) => {
                    console.log(arg)
                })
            }))
    
            first().then((arg) => {
                console.log(arg);
            });
            console.log(4);
    //374125
    

    第一步同步执行:执行第一个promise输出3,接着执行第二个promise里面接着输出7,

    setTimeout(() => {
                        console.log(5);
                        resolve(6)
                    }, 0)
    

    定时器放在宏队列

    往下执行

    p.then((arg) => {
             console.log(arg)
     })
     //这个上面执行 resolve(1)的时候就有结果了,arg的值为1,有未成功-》成功的状态变化,所以这个加入到微队列里面[then2]
    
      resolve(2) //这句话代表了第一个promise的状态右不成功到成功
       first().then((arg) => {
                console.log(arg);
        });
    //这个第一个promise的then方法的回调函数也放在队列里,现在队里 [then2(1),then2(2)]
    

    现在执行console.log(4) ,目前执行第一遍输出的是 3,7,4

    现在取出微队列 输出 1,2

    最后执行定时器 输出5,那个定时器的resolve(6)没有任何意义,因为改变了一次就不能在改变状态了

    所以随后输出的是3,7,4,1,2,5

    第三题

    记住一句话:状态只能改变一次,所以就resolve('success1');是有效的

    const promise = new Promise((resolve, reject) => {
        resolve('success1');
        reject('error');
        resolve('success2');
    });
    
    promise.then((res) => {
        console.log('then:', res);
    }).catch((err) => {
        console.log('catch:', err);
    })
    //then: success1
    
    
    resolve 函数将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
    
    reject 函数将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
    
    而一旦状态改变,就不会再变。
    所以 代码中的reject('error'); 不会有作用。
    
    Promise 只能 resolve 一次,剩下的调用都会被忽略。
    所以 第二次的 resolve('success2'); 也不会有作用。
    
    

    5、参考链接

    MDN promise

    MDN promise使用

    promise详解

    promise面试题

    promise参考视频

  • 相关阅读:
    WebUpLoder 能自动预览,能多实例,包括后台demo
    ajax请求总是进入Error里
    c#_1:后台post请求
    Echarts_1:水平柱体
    Hello World!
    python正则表达式
    python web.py出现ValueError: need more than 1 value to unpack
    web.py端口被占用的错误
    github commit时出现 Please tell me who you are.以及项目名称管理
    打飞机小游戏 python+pygame
  • 原文地址:https://www.cnblogs.com/wlhappy92/p/11899633.html
Copyright © 2011-2022 走看看