14.回调地狱与 Promise

回调地狱

为了保证异步代码的执行顺序,将异步代码嵌套到回调函数中,当异步的方法多了,就会产生回调地狱(callback hell),这样的代码丑陋且难以维护。


14.回调地狱与 Promise_第1张图片
回调地狱

读取三个文件 - 无法保证顺序的代码:

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  console.log(data)
})

fs.readFile('./data/b.txt', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  console.log(data)
})

fs.readFile('./data/c.txt', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  console.log(data)
})

读取三个文件 - 通过回调嵌套的方式来保证执行顺序:

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  console.log(data)
  fs.readFile('./data/b.txt', 'utf8', function (err, data) {
    if (err) {
      throw err
    }
    console.log(data)
    fs.readFile('./data/c.txt', 'utf8', function (err, data) {
      if (err) {
        throw err
      }
      console.log(data)
    })
  })
})

ES6 Promise

参考文档:http://es6.ruanyifeng.com/#docs/promise

为了解决以上编码方式带来的问题(回调地狱嵌套),所以在 EcmaScript 6 中新增了一个 API:Promise

  • Promise 是一个容器,容器中保存着某个异步任务操作的结果。从语法上说,Promise 是一个对象,从它可以获取异步任务操作的消息。Promise 本身不是异步,但内部往往都是封装一个异步任务。
    • 三种任务状态
      • 默认状态:Pending 进行中
      • 任务成功:Resolved 已完成
      • 任务失败:Rejected 已失败
    • 两种状态变化
      • Pending => Resolved
      • Pending => Rejected

基本用法:

var fs = require ('fs')

// 创建 Promise 容器
var promise = new Promise(function (resolve, reject) { // 这里的 function 是异步任务
  if (err) {
    reject(err) // 任务失败,把容器的 Pending 状态变为 Rejected
  } else {
    resolve(data) // 任务成功,把容器的 Pending 状态变为 Resolved
  }
})

promise
  .then(function (data) { 
    console.log(data) // 第一个参数: function 就是容器中的 resolve 函数
  }, function (err) {
    console.log('读取文件失败了',err) // 第二个参数: function 就是容器中的 reject 函数
  })

读取三个文件 - 使用 Promise 的方式来保证执行顺序:

var fs = require('fs')

var p1 = new Promise(function (resolve, reject) {
  fs.readFile('./data/a.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

var p2 = new Promise(function (resolve, reject) {
  fs.readFile('./data/b.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

var p3 = new Promise(function (resolve, reject) {
  fs.readFile('./data/c.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

p1
  .then(function (data) {
    console.log(data)
    return p2
  }, function (err) {
    console.log('读取a文件失败了', err)
  })
  .then(function (data) {
    console.log(data)
    return p3
  }, function (err) {
    console.log('读取b文件失败了', err)
  })
  .then(function (data) {
    console.log(data)
    console.log('end')
  }, function (err) {
    console.log('读取c文件失败了', err)
  })

解读:

  • 当 p1 读取成功的时候, 当前 then 函数中 return 的结果就可以在后面的 then 中 function 接收到,当 return 123 后面就接收到 123,return 'hello' 后面就接收到 'hello',没有 return 后面收到的就是 undefined。
  • 当我们 return Promise 对象 p2 的时候,后续的 then 中的方法的第一个参数会作为 p2 的 resolve
    方法,第二个参数作为 p2 的 reject 方法。

封装 Promise API

上面的代码示例解决了嵌套代码不美观的问题,但是没有解决代码重复冗余的问题,因此我们这里把 Promise 封装成可以重复调用的 API

var fs = require('fs')

function pReadFile(filePath) {
  return new Promise(function(resolve, reject) {
    fs.readFile(filePath, 'utf8', function (err, data) {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

pReadFile('./data/a.txt')
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/b.txt')
  }, function (err) {
    console.log('读取a文件失败了', err)
  })
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/c.txt')
  }, function (err) {
    console.log('读取b文件失败了', err)
  })
  .then(function (data) {
    console.log(data)
  }, function (err) {
    console.log('读取c文件失败了', err)
  })

你可能感兴趣的:(14.回调地狱与 Promise)