javascript异步编程

  • 什么是回调地狱(回调金字塔)?

    由于javascript是单线程执行的,所以在javascript中会经常使用异步回调函数,异步回调函数只有在请求完成之后才会执行,多层嵌套的回调函数不仅影响性能,也会降低代码阅读率和增加代码维护成本。例如以下代码:

    var sayhello = function(name, callback){
        setTimeout(function(){
            console.log(name);
            return callback(null);
        },1000);
    }
    console.log("回调函数开始");
    sayhello("回调函数1", function(err){
        sayhello("回调函数2", function(err){
            sayhello("回调函数3", function(err){
                console.log("回调结束");
            });
        });
    });
    

    执行结果如下:

    回调函数开始
    回调函数1
    回调函数2
    回调函数3
    回调结束
    

    以上代码中三层嵌套的回调函数,代码看起来非常冗余,随着嵌套层数的增加,代码冗余度也会越来越大,性能和可维护性也会降低,所以不建议通过“回调地狱”的形式实现上述功能。

  • 怎么解决回调地狱?

    解决回调地狱的常见方法有以下4种:

    • 发布者订阅模式

      发布订阅模式是利用一个消息中心,发布者发布一个消息给消息中心,订阅者从消息中心订阅该消息,类似于 vue 的父子组件之间的传值。

      发布订阅模式demo如下:

      //订阅done事件
      $('#app').on('done',function(data){
       console.log(data)
      })
      
      //发布事件
      $('#app').trigger('done,'haha')
      
    • Promise

      Promise 实际就是一个对象, 从它可以获得异步操作的消息,Promise 对象有三种状态,pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的状态一旦改变之后,就不会在发生任何变化,通过链式调用的方式调用回调函数。

      Promise封装异步请求demo如下:

      export default function getMethods (url){
        return new Promise(function(resolve, reject){
          axios.get(url).then(res => {
            resolve(res)        
          }).catch(err =>{
            reject(err)      
          })   
        })
      }
      
      getMethods('/api/xxx').then(res => {
         console.log(res)
      },err => {    
         console.log(err)
      })
      
    • Generator(ES6)

      Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,使用该对象的 next() 方法,可以遍历 Generator 函数内部的每一个状态,直到 return 语句。

      形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式, yield是暂停执行的标记。

      next() 方法遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

      Generator 的 demo:

      function *generatorDemo() {  
        yield 'hello'; 
        yield  1+4;  
        return 'ok';
      }
      
      var demo = generatorDemo()
      
      demo.next()   // { value: 'hello', done: false } 
      demo.next()   // { value: 5, done: false } 
      demo.next()   // { value: 'ok', done: ture } 
      demo.next()   // { value: undefined, done: ture } 
      
    • async await(ES7)

      async函数返回的是一个 Promise 对象,可以使用 then 方法添加回调函数,async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

      await命令后面返回的是 Promise 对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。

      async await 内部代码从右向左,先执行await等待的结果(await 右侧),发现有await关键字,则让出线程,阻塞代码。await要等待的结果,如果不是 promise , await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为 await表达式的结果。

      如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。

      async 的 demo:

      async function demo(){   
        try{
          await new Promise(function(resolve, reject) {     
          // do something  ...  
        }); 
        }catch (err) {
           console.log(err);  
        }
      }
      
      demo().then(data =>{
          console.log(data)  
      })
      
  • 异步编程测试题目:

    async function async1() {
       console.log( 'async1 start')
       await async2()
       console.log( 'async1 end' )
    }
    async function async2() {
       console.log( 'async2')
    }
    console.log( 'script start')
    setTimeout( function () {
      console.log( 'setTimeout' )
    }, 0)
    async1();
    new Promise( function( resolve ) {
       console.log( 'promise1' )
       resolve();
    }).then( function() {
       console.log( 'promise2')
    })
    console.log( 'script end')
    

    在浏览器中的输出结果如下:

    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    setTimeout
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值