由于Javascript执行环境是“单线程”的,想要实现异步编程,就会用到回调函数。然而如果一些ajax请求之间存在依赖,或者服务端使用node进行大量的io操作时就会出现回调函数嵌套的情况,代码就变成了横向发展,不利于阅读,维护起来麻烦。

//回调函数嵌套的情况
fs.readFile(fileA, function (err, data) {
  fs.readFile(fileB, function (err, data) {
    // ...
  })
})


解决回调函数嵌套问题可以使用:

  1. Generator
  2. co模块
  3. Promise
  4. Async

ES7 中有了更加标准的解决方案,新增了 async/await 两个关键词。async 可以声明一个异步函数,此函数需要返回一个 Promise 对象。await 可以等待一个 Promise 对象 resolve,并拿到结果。 在这里主要介绍async解决回调函数的问题,因为相对于前面的3种方法,async实现最简洁,符合语义,几乎没有语义不相关的代码。

异步数据加载

b函数里异步请求参数的值依赖a函数的数据,需要等a函数的异步操作完成后才能执行b函数

var $ = require('jquery')

var a = function(){
  return new Promise(function(resolve, reject){
    $.ajax({  
      url:'',
      type: 'get',
      dataType: 'json',
      success: function(data){
        resolve(data)
      },
      error: function(xhr){
        reject(xhr)
      }
   })
  })
}

var b = function(dataA){
  return new Promise(function(resolve, reject){
    $.ajax({  
      url:'',
      type: 'get',
      data: dataA.value,
      dataType: 'json',
      success: function(data){
        resolve(data)
      },
      error: function(xhr){
        reject(xhr)
      }
   })
  })
}

// 调用
;(async function (){
  var dataA = await a() //得到a函数的数据
  var dataB = await b(dataA) //得到b函数的数据
  // ...
})();

上面通过一个匿名函数,前面加上一个async(表示一个异步函数), 里面使用await语句(相当于暂停标志),就可以像写同步的代码一样解决回调函数嵌套的问题。

实现sleep函数

function sleep (timeout){
  return new Promise(function(resolve){
    setTimeout(function(){
          resolve()
    }, timeout)
  })
}
;(async function(){
  console.log(new Date())
  await sleep(3000)
  console.log(new Date())
})()

输出第一个log信息后,遇到await语句,执行sleep方法,然后会等待3秒才继续执行下面的代码。

由于它是ES7的方法,想要在前端或者node服务端使用需要babel转成ES5,或者使用polyfill