美文网首页前端必备性能优化
#Promise async/await总结

#Promise async/await总结

作者: 扶不起的蝌蚪 | 来源:发表于2020-03-14 17:39 被阅读0次

    1. JS中的异步操作哪些?

    • 定时器settimeout
    for (var i = 0; i <10; i++) {  
      setTimeout(()=>{  
        console.log(i);        
      }, 0);
    }
    //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10  
    
    

    问:下面打印的结果是?

    console.log('1')
    setTimeout(()=>{console.log('2')},0)
    console.log('3')
    ------------------------
    // 1
    // 3
    // 2
    
    • 事件绑定addEventListener,onclick
    for (var i = 0; i < tabList.length; i++) {
        //tabList[i] <=>每一轮循环当前要操作的LI DOM对象
        tabList[i].onclick = function () {
            alert(i);
            changeTab(i);//=>需要把当前点击的这个LI的索引传递进来
        }
    }*/
    当绑定完毕事件后,进行点击发现i全都是3了
    
    • AJAX一般采取异步处理
    • promise
    • async/await

    2. 处理异步的方式有哪些?

    2.1 回调函数

    回调callback是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。( B函数被作为参数传递到A函数里,在A函数执行完后再执行B )
    假定有两个函数f1和f2,f2等待f1的执行结果,f1()-->f2();如果f1很耗时,可以改写f1,把f2写成f1的回调函数:

    function f1(callback){
      setTimeout(function () {
        callback(); // f1的任务代码
      }, 1000);
    }
    f1(f2);  // 执行
    

    采用回调的方式,把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

    回调函数是异步编程最基本的方法,其优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。

    注意 区分 回调函数和异步,回调是实现异步的一种手段,并不一定就是异步。
    回调也可以是同步,如:

    //下面没有任何有关异步的函数操作,所以是同步的
    function A(callback){
        console.log("I am A");
        callback();  //调用该函数
    }
    function B(){
       console.log("I am B");
    }
    A(B);
    

    2.2 事件监听

    采用事件驱动模式,任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
    监听函数有:onbindlistenaddEventListenerobserve
    举例,为f1绑定一个事件(jquery写法):f1.on('done',f2);即当f1发生done事件,就执行f2。

    function f1(){
        settimeout(function(){
           // f1的任务代码
           f1.trigger('done');  // 执行完成后,立即触发done事件,从而开始执行f2
        },1000);
    }
    

    优点:易理解,可绑定多个事件,每一个事件可指定多个回调函数,可以去耦合,有利于实现模块化
    缺点:整个程序都要变成事件驱动型,运行流程会变得不清晰

    2.3 发布订阅

    2.4 Promise

    2.4.1 Promise基本概述
    • promise的三种状态pending,fulfilled,rejected,状态只能是3个中的一种
    • 状态改变结果就定死了,不能再改变状态
    • resolve,reject其实就是你在声明Promise中的一道往外传递参数的.then()中的参数就是你从这个门往外resolve的参数,.cathch()中的参数就是你从这个门往外reject的参数
    2.4.2 基本用法

    new Promise时候会立刻执行

    new Promise.png

    控制台能反映当前promise的状态,因为我没有resolve或reject,状态就不会发生改变

    new Promise((resolve,reject)=>{console.log('1')})
    //=> 1
    

    里面加入resolve我们看看他的状态


    resolve.png

    可以看到Promise的状态变成了resolve

    return new Promise

    return new Promise一般情况下我们把Promise封装到一个函数中,在我们需要处理异步的时候进行调用,就需要进行return操作,当我们调用函数的时候实际上是进行上面的new Promise立即执行,new promise还是同步处理的

    const ajaxPromise=  param => {
      return new Promise((resovle, reject) => {
        $.ajax({
          ...
          "success": res => {
            resovle(res);
          },
          "error": err => {
            reject(err);
          }
        })
      })
    }
    

    promise中return的问题

    一般情况下我们只考虑一种参数状态处理的时候就不需要return,比如上面的代码块,如果进入success时,success的回调函数就只有一局resolve,而且这是最后一句话,执行完就resolve出去了,就不用进行return操作,但是如果回调的时候考虑返回值的多种状态就需要进行return,否则resolve后面的函数仍会执行

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                console.log('resolve前面的语句')
                resolve('resolve的结果')
                console.log('resolve后面的语句')
            }
            else{
                reject
            }
        })
    }
    AA(3).then(res=>{console.log(res)})
    //=>resolve前面的语句
    //=>resolve后面的语句
    //=>resolve的结果
    

    return在起到中断返回的作用,但是是promise中then()的参数只能接受由resolve出去的,return的参数是无效的

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                return '3'
                resolve('resolve的结果')
            }
            else{
                reject
            }
        })
    }
    AA(3).then(res=>{console.log(res)})
    //=>  无反应
    

    then的使用

    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
        })
    }
    AA(3).then(res1=>{return AA(3)})
    .then(res2=>{console.log(res2)})
    //=> resolve的结果
    

    只要在then中return出去的都会被包装为新的Promise实例,无论你return出去的是什么东西,并且return的值不可能return到promise外面被其他变量接收(解决办法:1.callback 2.async swait)

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
        })
    }
    AA(3).then(res=>{return '3'})
    .then(res=>{console.log(res)})
    //=> 3
    

    后续链式的.then只能接受resolve到的东西,而不会一个new Promise函数

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
        })
    }
    AA(3)
    .then(res=>{return AA(4)})
    .then(res=>{console.log(res)})
    .catch(res=>{console.log('catch ' + res)})
    //=>catch 4
    

    catch的使用

    catch用于捕获reject从而达到rejected状态

    .catch实际上是.then里面的第二个参数

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
        })
    }
    AA(3)
    .then(res=>{return Promise.reject('在catch捕获')})
    .then(res=>{console.log('then' + res)},err=>{console.log('第二个参数位置' + err)})
    //=>第二个参数位置在catch捕获
    

    若不放回调里面,cathc无法捕获是哪个位置发生的异常

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
            reject(4)
        })
    }
    AA(3)
    .then(res=>{return AA(4)})
    .then(res=>{console.log(res)})
    .catch(res=>{console.log(res)}) 
    .catch(res=>{console.log(res)})
    

    如果我们想要第二个catch捕获,不用async写的话只能写在回调函数里面,这是promise的缺点(轻微回调地狱)

    function AA(a){
        return new Promise((resolve,reject)=>{
            if(a==3){
                resolve('resolve的结果')
            }
            reject(4)
        })
    }
    AA(3)
    .then(res=>{
            AA(4)
            .then(res=>{
                console.log(res)
            })
            .catch(res=>{
                console.log(res)
            })
        })
    .catch(res=>{console.log(res)})
    

    如果涉及多种catch判别,回调现象就很严重了

    
    distance(this.hazardInfo.hazardLatitude, this.hazardInfo.hazardLongitude)//定位打卡
                    .then(res => {
                        uploadMedia(this)//上传媒体
                            .then(res => {
                                // console.log(res);
                                this.$http.post('URL', this.hazardInfo) //上传表单
                                    .then(res => {
                                        console.log(res);
                                        uni.showToast({
                                            icon: 'none',
                                            title: '上传成功',
                                            success() {
                                                setTimeout(() => {
                                                    uni.navigateBack({
                                                        delta: 1
                                                    });
                                                }, 2000);
                                            }
                                        });
                                    })
                                    .catch(err => {
                                        console.log(err);
                                        uni.showToast({ icon: 'none', title: '表单上传失败' });
                                    });
                            })
                            .catch(() => {
                                uni.showToast({ icon: 'none', title: '媒体上传失败' });
                            });
                    })
                    .catch(type => {
                        switch (type) {
                            case 0:
                                uni.hideLoading();
                                uni.showToast({
                                    icon: 'none',
                                    title: '当前未在距离范围,请调整你的位置!',
                                    position: 'bottom'
                                });
                                break;
                            case 1:
                                uni.hideLoading();
                                uni.showToast({
                                    icon: 'none',
                                    title: '定位失败,请开始定位权限',
                                    position: 'bottom'
                                });
                            default:
                                break;
                        }
                    });
            }
    

    如果写成链式调用,你会发现永远是第一个catch捕获,你可能会说,我就在一个catch里面进行res的类型判断不就行了,那么我问你,如果涉及到多个AJAX的promise错误,500的错误都是一样的,你怎么进行类别的判断。如果是你自己封装的promise到可以,如果是用axios这种第三方库是肯定不行的。

    3. async/await

    由上面可以总结Promise有两个缺点

    • 涉及多种catch类型捕获,需要写到回调函数里面,如果链式写法会永远被第一个catch捕获
    • then里面的值无法return出去,只能被下一个then进行操作

    3.1 async

    async函数永远返回的是promise对象,对,这种机制很像promise中then返回出去的东西,他只能是一个promise对象,无论你返回的是什么

    async function testAsy(){
      return 'hello world';
    }
    let result = testAsy(); 
    console.log(result)
    
    promise状态.png

    那么意味着可以对result进行
    then操作

    async function testAsy(){
      return 'hello world';
    }
    let result = testAsy(); 
    result.then(res=>{console.log(res)})
    //=>  hello world
    

    catch操作

    async function testAsy(){
      return Promise.reject('error')
    }
    let result = testAsy(); 
    result
    .then(res=>{console.log(res)})
    .catch(res=>{console.log(res)})
    //=>error
    

    所以当没有await语句执行async函数,它就会立即执行,返回一个Promise对象,非阻塞,与普通的Promise对象函数一致

    3.2 await

    await如果等待的是Promise对象,则返回Promise的处理结果;如果是其他值,则返回该值本身await并不会进行等待。并且await会暂停当前async function的执行,等待Promise的处理完成。
    await可以把primise返回的值给指定变量这是Promise语法无法实现的

    如果await等的是Promise
    function testAsy(x){
       return new Promise(resolve=>{setTimeout(() => {
           resolve(x);
         }, 3000)
        }
       )
    }
    async function testAwt(){    
      let result =  await testAsy('hello world');
      console.log(result);    
      console.log('tangj')    
    }
    testAwt();
    console.log('tangSir')  
    //=>tangSir
    //=>3秒钟之后出现hello world
    //=>tangj
    
    如果await等的是其他值

    await等待是是其他的值,那么个同步任务没有区别

    function testAsy(x){
       return new Promise(resolve=>{setTimeout(() => {
           resolve(x);
         }, 3000)
        }
       )
    }
    async function testAwt(){    
      let result =  await 'no Promise';
      console.log(result);
      let res =  await testAsy('promise');
      console.log(result);    // 3秒钟之后出现hello world
      console.log(res )
      console.log('tangj')   // 3秒钟之后出现tangj
    }
    testAwt();
    console.log('tangSir')  //立即输出tangSir
    //=>tangSir
    //=>no Promised
    //=>3秒钟之后出现no Promise
    //=>promise
    //=>tangj
    

    await缺点:是只能接受resolve的结果,无法处理catch的结果,但是会对reject进行报错,从而影响async函数的继续执行

    function testAsy(x) {
        return new Promise((resolve,reject) => {
            if (x == 1) {
                resolve('1')
                return 
            }
            reject(2)
        })
    }
    
    async function testAwt(){    
       let res  = await testAsy(2)
        console.log(res  )
        console.log(3)
    }
    testAwt()
    
    报错 不会打印下面的res和3.png

    如果不想影响后面异步的进行(比如进入web首页,多页面迸发ajax,不能让其中一个错误的AJAX影响后面的AJAX请求),以下有两种操作

    • try catch
    • .catch
      下面我只对.catch进行说明,try catch在我看来反而还不如promise美观
    function testAsy(x) {
        return new Promise((resolve,reject) => {
            if (x == 1) {
                resolve('1')
                return 
            }
            reject(2)
        })
    }
    
    async function testAwt(){    
       let res  = await testAsy(2).catch(err=>{console.log(err)})
        console.log(res  )
        console.log(3)
    }
    testAwt()
    //=>  2
    //=> undefined
    //=> 3
    

    那么我上面的promise嵌套回调就可以进行下面的写法

    async start(){
        let location = await distance(this.hazardInfo.hazardLatitude, this.hazardInfo.hazardLongitude)
    
         let meida = await uploadMedia()
         let form = await this.$http.post('URL', this.hazardInfo)
    }
    记得把原来的各种catch处理放到封装的promise函数的reject之前就行了
    

    如果我们想既要catch异常,又不想继续往下执行,有两种办法

    • 解构赋值,适用于promise返回的为对象
    let { data } = await this.$http.post('/api', { username: username, password: password }}).catch(err => {
                    this.isRotate = false;
                });
    console.log(data)
    console.log('1')
    //=>TypeError: undefined is not an object (evaluating '_ref.data')  无法解构导致报错,无法往下进行
    
    • 用普通变量接promise返回值,适用于任何类型的返回值
    let  data  = await this.$http.post('/api, { username: this.username, password: this.password }}).catch(err => {
                    this.isRotate = false;
                });
    console.log(data)
    console.log('1')
    if(data == undefined )  return 
    console.log('2')
    //=>undefined
    //=>1
    

    相关文章

      网友评论

        本文标题:#Promise async/await总结

        本文链接:https://www.haomeiwen.com/subject/uspgshtx.html