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

读取三个文件 - 无法保证顺序的代码:
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)
})
网友评论