/* t05.js */
// 模拟Promise 的then 方法的链式调用(第三版,ES5 非箭头函数写法)
// 本程序只模拟Promise 链式调用以理解使用其API,对异步函数的顺序执行不做模拟。
/*
有两点需要注意的:
1、在定义的时候将一些变量注入给回调函数;
2、在回调函数真正运行时可通过形参获取之前注入的变量。
*/
// 用构造函数定义个类PP 模拟Promise 类
function PP(id, pending) {
// 多增加一个id 属性,以区分是哪个PP 实例
this.id = id;
// Promise 有三种状态:pending、resolved、rejected
this.state = 'pending'; // 初始化状态为pending
// 用bind 方法返回的绑定了PP 类的实例化对象的新函数注入给回调函数pending
pending(PP.resolve.bind(this), PP.reject.bind(this));
// 使用本构造函数实例化时,回调函数可以通过形参获取以上两个方法
}
// 定义类PP 的静态方法resolve 和reject
// reject 方法主要作用就是改变本对象的state 为rejected,错误信息原封不动保存下来
PP.reject = function(error) {
console.log(this.id, '1st Error: ' + error);
this.error = error; // 保存错误信息
this.state = 'rejected'; // 改变本对象的状态为rejected
};
// resolve 方法主要作用就是改变本对象的state 为resolved,数据信息原封不动保存下来
PP.resolve = function(value) {
console.log(this.id, '1st Value: ' + value);
this.value = value; // 保存返回数据
this.state = 'resolved'; // 改变本对象的状态为resolved
};
// 哪个PP 对象调用then,方法里的this 就指向谁,所以将其定义在原型对象中避免每个实例都有相同的定义代码
PP.prototype.then = function(resolve, reject) {
console.log('ID', this.id, 'STATE', this.state);
if (this.state == 'rejected') {
// 将本对象的error 即new PP 时保存原封不动的错误信息注入到reject 方法中,调用时可通过形参获取
reject(this.error);
// 默认return undefined,由于报错不返回PP 对象,后续的.then 都不能执行
} else {
// 将本对象的value 即new PP 时保存原封不动的数据注入到resolve 方法中,调用时可通过形参获取
let result = resolve(this.value);
// 判断resolve 的返回值,如是PP 对象则将其返回,否则将result 保存到本对象的value 中
if (result instanceof PP) {
return result;
} else {
this.value = result;
return this;
}
}
};
// ---------------------------------------------------------------------------
// 自定义函数,模拟会有可能产生错误的函数
// 第一个参数是错误标识,非0 表有错误
// 第二个参数是返回数据,这里简单点,给它设置什么就返回什么
// 第三个参数是回调函数,在下面调用时,有错误就调用PP 对象的reject 方法
function getData(eflag, data, callback) {
callback(eflag, data);
}
// 调用测试:创建PP 实例对象p1,回调函数中调用getData 函数
// 形参resolve 和reject 就对应定义PP 时注入的resolve_fn 和reject_fn
// 跟Promise 对象一样,new 的时候就会执行回调函数
var p1 = new PP(101, function(resolve, reject) {
// 设置getData 第一个参数非0 即表示有错误产生
getData(0, 'WAHAHA', function(err, data) {
console.log('getData ', ' | err:', err, ' | data:', data);
if (err != 0) { // 如果有错误发生则调用本对象的reject 方法
reject(err);
} else {
resolve(data); // 如果一切正常则调用本对象的resolve 方法
}
})
});
p1 // PP 类原型的then 方法
// 第一个回调函数会设置成PP 对象的resolve 方法,即初始化的resolve 方法会被覆盖
// 第二个回调函数会设置成PP 对象的reject 方法,可省略第二个参数
.then(
function(value) {
console.log('Value: ', value);
return 110;
})
.then(
function(value) {
console.log('Value: ', value);
// 返回一个新的PP 对象,下一次调用的是该对象的then 方法
return new PP(202, function(resolve, reject) {
// 设置getData 第一个参数非0 即表示有错误产生
getData(99, 'NEWPP', function(err, data) {
console.log('getData ', ' |err:', err, ' |data:', data);
if (err != 0) { // 如果有错误发生则调用本对象的reject 方法
reject(err);
} else {
resolve(data); // 如果一切正常则调用本对象的resolve 方法
}
});
})
})
.then(function(value) {
console.log('Value: ', value);
},
function(error) {
console.log('Error: ', error);
});
/*
保存以上JavaScript 为t05.js
同一目录下新建文件t05.html 内容为:
<script src="t05.js"></script>
再在浏览器打开t05.html 即可在开发者工具的Console 里看到效果。
*/
运行效果
L@H ES6$ node t05
getData | err: 0 | data: WAHAHA
101 '1st Value: WAHAHA'
ID 101 STATE resolved
Value: WAHAHA
ID 101 STATE resolved
Value: 110
getData |err: 99 |data: NEWPP
202 '1st Error: 99'
ID 202 STATE rejected
Error: 99