• JS——异步编程的六种解决方案


    将需要在异步任务后执行的操作,作为参数传入到异步操作中,当异步操作执行完成后,调用该参数执行后面的操作

    ajax(url,()=>{})
    

    回调函数简单,容易理解和实现;但回调函数的缺点就是,容易写出回调地狱

    多个异步操作需要规定执行顺序时产生回调地狱

    回调地狱导致代码不容易阅读和维护,各个部分高度耦合,使得程序结构混乱,流程那一追踪

    ajax(url,()=>{
        //处理逻辑
        ajax(url1,()=>{
            //处理逻辑
            aja(url2,()=>{
                //处理逻辑
            })
        })
    })
    
    • 事件监听

      异步任务的执行不取决于代码的顺序,而是取决于某个事件是否发生,事件驱动

      f1.on('done',f2)
      //f1 绑定事件 done,只有当f1发生done事件的时候才执行f2
      function f1(){
          setTimeout(function(){
              //……
              f1.trigger('done')	//触发done事件,f2立即执行
          }, 1000)
      }
      

      这种方式容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以“去耦合”,有利于实现模块化。缺点就是整个程序都会变成事件驱动型,运行流程变得很不清晰

    • 发布订阅

      假定存在一个"信号中心",某个任务执行完成,就想信号中心"发布(publish)"一个信号,其他任务可以向信号中心"订阅(subscribe)"这个信号,从而知道什么时候自己可以执行。这就是发布订阅模式,又称"观察者模式"

      jQuery.subscribe('done',f2)
      //f2 向订阅中心订阅了 done 信号
      function f1(){
          setTimeout(function(){
              //……
              jQuery.publish('done')	//执行完成后向订阅中心发布done信号
          },1000)
      }
      
      jQuery.unsubscribe('done',f2)	//f2执行完成后可以取消订阅
      

      这种方式与"事件监听类似",但明显后者优于前者。因为可以通过查看"消息中心",了解存在多少信号,每个信号有多少订阅者,从而监听程序的运行

    • Promise

      ES6引入的异步编程解决方案

      graph LR a(回调函数)-- 复杂异步问题 -->b(回调地狱) b-->c(代码难以阅读和维护) c-->d(Promise)
      /*##回调函数异步解决方案*/
      setTimeout(()=>{
          console.log('hello world');
          setTimeout(()=>{
              console.log('hello vueJs');
              setTimeout(()=>{
                  console.log('hello Promise');   //回调函数产生的回调地狱
              },1000)
          },1000)
      }, 1000)
      /*##Promise异步解决方案*/
      new Promise((resolve, reject)=>{
          //第一次异步请求
          setTimeout(()=>{
              //成功的时候调用resolve
              resolve('hello world')   //通过resolve函数将结果返回到外部 then 处理
          }, 1000)
      }).then((data)=>{
          //处理第一次异步请求
          console.log(data);
          //第二次异步请求
          return new Promise((resolve,reject)=>{
              setTimeout(()=>{
                  resolve()
              },1000)
          })
      }).then(()=>{
          //处理第二次异步请求
          console.log('hello vueJs');
          //第三次请求
          return new Promise((resolve,reject)=>{
              setTimeout(()=>{
                  resolve()
              },1000)
          })
      }).then(()=>{
          //处理第三次异步请求
          console.log('hello Promise');
      })
      
      
      • Promise 怎么使用

        1. 将异步操作放进 Promise 中

        2. new Promise(回调函数)

          new -> 构造函数(1.保存一些状态信息,2.执行传入的参数(即回调函数))

        3. 回调函数:(resolve,reject)=>{异步操作}

        4. resolve 和 reject 本身又是函数

        5. 通过 resolve 和 reject 将数据返回到外部 .then() .catch处理 --> 链式编程

          可以正确和错误的回调都在 then() 中处理

          new Promise((resolve,reject)=>{
              setTimeout(()=>{
                  //遇到错误调用reject,将错误返回到外部 catch 处理
                  reject('hello error')
              },1000)
          }).then(data=>{
              console.log(data);
          },err=>{
              console.log(err);
          })
          
      • Promise 的三种状态

        pending: 等待状态,比如正在进行的网络请求,或定时器没有到时间
        fulfill: 满足状态,主动回调 resolve 时,就处于满足状态,并且回调 .then()
        reject: 拒绝状态,主动回调 reject 时,就处于拒绝状态,并且回调 .catch()

      • Promise的链式调用

        graph LR a(返回新的Promise对象)-->b(返回Promise.resolve) b-->c(直接返回数据)
        /*最开始的链式调用*/
        new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('hello1')
            })
        }).then((data)=>{
            console.log(data,"第一层数据处理");
            return new Promise(resolve => {
                resolve(data+'1')
            })
        }).then(data=>{
            console.log(data,'第二层数据处理');
            return new Promise(resolve => {
                resolve(data+'1')
            })
        }).then(data=>{
            console.log(data,'第三层数据处理')
        })
        
        
        /*简化new的链式调用*/
        new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('hello1')
            })
        }).then((data)=>{
            console.log(data,"第一层数据处理");
            // return Promise.resolve(data+'1')
            return Promise.reject('error')  //抛出错误信息的简化方式
        }).then(data=>{
            console.log(data,'第二层数据处理');
            return Promise.resolve(data+'1')
        }).then(data=>{
            console.log(data,'第三层数据处理')
        }).catch(err=>{
            console.log(err);
        })
        
        
        /*最终简化版*/
        new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('hello1')
            })
        }).then((data)=>{
            console.log(data,"第一层数据处理");
            // return data+'1'
            throw 'error'   //抛出错误的简写方式
        }).then(data=>{
            console.log(data,'第二层数据处理');
            return data+'1'
        }).then(data=>{
            console.log(data,'第三层数据处理')
        }).catch(err=>{
            console.log(err);
        })
        
      • Promise.all

        当一个任务需要多个异步任务的结果才能执行时

        /*1. 通过回调函数实现*/
        let isResult1 = false
        let isResult2 = false
        $ajax({
            url: '',
            success: function () {
                console.log('结果1');
                isResult1 = true
                handleResult()
            }
        })
        $ajax({
            url: '',
            success: function () {
                console.log('结果2');
                isResult2 = true
                handleResult()
            }
        })
        function handleResult() {
            if(isResult1 && isResult2){
                //逻辑处理
            }
        }
        
        /*2. Promise的all方法,同时进行多个异步操作*/
        Promise.all([
            new Promise((resolve,reject)=>{
                $ajax({
                    url: 'url1',
                    success: function (data) {
                        resolve(data)
                    }
                })
            }),
            new Promise((resolve,reject)=>{
                $ajax({
                    url: 'url2',
                    success: function (data) {
                        resolve(data)
                    }
                })
            }),
        ]).then(results=>{
            results[0]  //第一个异步操作返回的结果
            results[1]  //第二个异步操作返回的结果
        })
        
    • 生成器 Generator/yield

      Generator与函数很像,但定义时多个 * 号,并且除了 return 以外,还可以用 yield 返回多次

      function* foo(x){
          yield x+1
          yield x+2
          return x+3
      }
      let f = foo(3);
      f.next()    //{value: 4, done: false}
      f.next()    //{value: 5, done: false}
      f.next()    //{value: 6, done: true}
      f.next()    //{value: undefined, done: true}
      //也可以直接用循环地跌 generator 对象
      for(var x fo foo(3)){
          console.log(x)
      }
      

      generator 函数的特性很适合用来处理异步操作因为它的每一步yield都是按顺序的

      function *fetch() {
          yield ajax(url, () => {})
          yield ajax(url1, () => {})
          yield ajax(url2, () => {})
      }
      let it = fetch()
      let result1 = it.next()
      let result2 = it.next()
      let result3 = it.next()
      
    • Aysnc/Await

      Aysnc 函数是 Generator 的优化

      Aysnc 是基于 Promise 实现的,Aysnc 返回一个 Promise 对象

      • Aysnc 对 Generator 的改进

        1. 内置执行器

          Generator 函数执行必须依靠执行器,所以才有了 co 函数库,而 aysnc 函数只带执行器。也就是说 async 函数的执行与普通函数的执行一模一样,只要一行

        2. 更广适用性

          yield 命令后面只能是 Thunk 函数或者 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和 原始类型的值(数值,字符串和布尔值,但这时等同于同步操作)

        3. 更好的语义

          async 和 await,比起 * 号和 yield ,语义更清楚了, async 表示函数里有异步操作, await 表示紧跟在后面的表达式需要等待结果

      • 缺点

        因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式

  • 相关阅读:
    【游普罗旺斯薰衣草庄园】诗一首
    【甘道夫】Win7环境下Eclipse连接Hadoop2.2.0
    STL_算法_区间的比較(equal、mismatch、 lexicographical_compare)
    JAVA基础之訪问控制权限(封装)
    Linux Android 多点触摸协议 原文出自【比特网】,转载请保留原文链接:http://soft.chinabyte.com/os/71/12306571.shtml
    block的知识点
    Zookeeper 集群搭建--单机伪分布式集群
    Zookeeper 四字命令 Four Letter Words
    Zookeeper权限acl,acl的构成 scheme与id
    Zookeeper命令行auth,digest
  • 原文地址:https://www.cnblogs.com/angle-yan/p/13651453.html
Copyright © 2020-2023  润新知