es6——promise(解决回调地狱的问题)
注意:里面一些执行报错结果,是为了演示,故意写错地址
首先,提出一个需求:要封装一个方法,使得能够读取文件,并将内容返回
先创建 3个 .txt 文件1.txt/2.txt/3.txt
,作为我们要读取的文件,内容自己瞎编
- fs 和 path 模块:
const fs = require('fs');
const path = require('path');
- 这是普通读取文件的方式:
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf-8', (err, dataStr) => {
if (err) throw err;
console.log(dataStr);
});
- 升级:
我们可以规定一下,callback 中,有两个参数,第一个参数,是失败的结果;第二个参数是成功的结果
同时规定:如果成功后,返回的结果,应该位于 callback 参数的第二个位置,此时,第一个位置由于没有出错,所以放一个 null;如果失败了,则第一个位置放置 Error 对象,第二个位置放置 undefined
function readFile(fpath, callback) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
// 如果报错了,则进入 if 分支
if (err) return callback(err);
// return 会返回 undefined
// return dataStr;
callback(null, dataStr);
})
}
/*var result = readFile(path.join(__dirname, './files/1.txt'));
console.log(result);*/
readFile(path.join(__dirname, './files/1.txt'), (err, dataStr) => {
// console.log(dataStr);
if (err) return console.log(err.message);
console.log(dataStr);
});
其次,需求升级:先读取文件1,再读取文件2,最后读取文件3
const fs = require('fs');
const path = require('path');
// 为了方便初学者理解,在这里我把原来 callback 分为 succCB 和 errCB
function getFileByPath(fpath, succCB, errCB) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return errCB(err);
succCB(dataStr);
})
}
- 好了,见证回调地狱的时候到了:由于现在是读取三个文件,可能没什么,假如一万个文件。。。。。。呵呵
// 回调地狱
getFileByPath(path.join(__dirname, './files/1.txt'), function (data) {
console.log(data);
getFileByPath(path.join(__dirname, './files/2.txt'), function (data) {
console.log(data);
getFileByPath(path.join(__dirname, './files/3.txt'), function (data) {
console.log(data);
});
});
});
现在引入主题:Promise
使用 ES6 中的 Promise,来解决回调地狱的问题;
注意:promise 只是单纯的解决回调地狱问题,并不是解决代码量;
- Promise 是一个构造函数,可以 new Promise() 得到一个 Promise 的实例;
- 在 Promise 上,有两个函数,分别叫做 resolve(成功后的回调函数) 和 reject(失败后的回调函数)
- 在 Promise 构造函数的 Prototype 属性上,有一个 .then() 方法,也就是说,只要是 Promise 构造函数创建的实例,都可以访问到 .then() 方法
- Promise 表示一个异步操作;每当我们 new 一个 Promise 的实例,这个实例,就表示一个具体的异步操作;
- 既然 Promise 创建的实例,是一个异步操作,那么这个异步操作的结果,只能有两种状态:
- 状态1:异步执行成功了,需要在内部调用 成功的回调函数 resolve 把结果返回给调用者;
- 状态2:异步执行失败了,需要在内部调用 失败的回调函数 reject 把结果返回给调用者;
- 由于 Promise 的实例,是一个异步操作,所以,内部拿到操作的结果后,无法使用 return 把操作结果返回给调用者;这时候,只能使用回调函数的形式,来把成功或失败的结果,返回给调用者;
- 可以在 new 出来的 Promise 实例上,调用 .then() 方法,【预先】为这个 Promise 异步操作,指定 成功(resolve) 和 失败(reject) 回调函数;
下面代码解释原理:
1
// 注意:这里 new 出来的 promise,只是代表 【形式上】的一个异步操作(空壳);
let promise = new Promise();
2
const fs = require('fs');
// 每当 new 一个 promise 实例的时候,就会立即执行这个异步操作中的代码
// 也就是说,new 的时候,除了能够得到一个 promise 实例之外,还会立即调用我们
// 为 Promise 构造函数传递的那个function,执行这个 function 中的异步操作代码;
let promise = new Promise(function () {
fs.readFile('./files/3.txt', 'utf-8', (err, dataStr) => {
if (err) throw err;
console.log(dataStr);
})
});
3
const fs = require('fs');
// 可以自己决定是否调用 Promise
function getFileByPath(fpath) {
let promise = new Promise(function () {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) throw err;
console.log(dataStr);
})
})
}
getFileByPath('./files/3.txt');
4
const fs = require('fs');
// 可以报具体错误
function getFileByPath(fpath) {
let promise = new Promise(function (resolve, reject) {
//*2
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
/*if (err) throw err;
console.log(dataStr);*/
if (err) reject(err);
resolve(dataStr);
})
});
return promise;
}
let p=getFileByPath('./files/.txt');
// 第一个是 resolve,第二个是 reject
//*1
p.then(function (data) {
console.log('执行成功:'+data);
},function (err) {
console.log('执行失败:'+err.message);
});
注意:执行步骤是先*1
再*2
// 优化写法
getFileByPath('./files/.txt').then(function (data) {
console.log('执行成功:' + data);
}, function (err) {
console.log('执行失败:' + err.message);
});
解决回调地狱的问题:
const fs = require('fs');
function getFileByPath(fpath) {
let promise = new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) reject(err);
resolve(dataStr);
})
});
return promise;
}
// 注意:通过 .then 指定回调函数的时候,成功的函数,
// 必须传,但是失败的回调可以省略
// 在上一个 .then 中,返回一个新的 promise 实例,可以继续用下一个 .then 来处理
getFileByPath('./files/1.txt')
.then(function (data) {
console.log(data);
// 读取文件2,下面的以此类推
return getFileByPath('./files/2.txt')
})
.then(function (data) {
console.log(data);
return getFileByPath('./files/3.txt')
})
.then(function (data) {
console.log(data);
});