1. 初级版
首先要确定promise
有什么特点:
promise
是一个类;new
一个promise
都需要传递一个执行器 执行器是立即执行的;resolve
reject
;promise
有三个状态 pending => fulfilled
表示成功了,pending => rejected
表示失败了;pending
的时候,才能更改状态;promise
都有一个then
方法。基于以上的特点,实现了下面一版promise
的实现:
const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 创建promise,executor会立即执行
this.value = undefined,
this.reason = undefined;
this.status = PENDIND
let resolve = (value) => {
// 确保成功了就不能失败
if(this.status===PENDIND) {
this.value = value;
this.status = FULFILLED;
}
}
let reject = (reason) => {
// 确保失败了就不能成功
if(this.status===PENDIND) {
this.reason = reason;
this.status = REJECTED;
}
}
// 这里可能会抛出异常,抛出异常也要执行失败
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// then方法会判断当前的状态
then(onfulfilled, onrejected) {
if(this.status===FULFILLED) {
onfulfilled(this.value);
}
if(this.status===REJECTED) {
onrejected(this.reason);
}
}
}
// 导出当前类 commonjs定义方式
module.exports = Promise;
引入自己实现的promise
来验证一下:
let Promise = require('./promise');
let p = new Promise((resolve, reject) => {
resolve("成功了");
// 如果抛出异常也会执行失败
throw new Error("失败");
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
打印值:
success 成功了
success 成功了
success 成功了
这时考虑一种情况:
let Promise = require('./promise');
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功了");
})
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
p.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
构造函数中有异步代码的情况,这个时候new Promise
会立即执行,但是setTimeout
属于宏任务,所以会先执行外部的p.then
方法,但是promise
现在的状态是pending
,所以再用自己写的promise
来执行一下,会发现什么都没有输出。
但是在规范的Promise
中不是这个效果,不引入自己的promise
,执行这一段代码会发现还是会输出:
success 成功了
success 成功了
success 成功了
可见存在异步的时候,会先把then
中的回调函数存起来,然后promise
的状态改变后,就立即执行存起来的回调函数,这是典型的发布订阅模式(发布订阅参见博文https://blog.csdn.net/zl13015214442/article/details/101382999),下面看怎么实现:
const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.value = undefined,
this.reason = undefined;
this.status = PENDIND;
// 定义两个数组存放回调函数
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if(this.status===PENDIND) {
this.value = value;
this.status = FULFILLED;
// 当调用到resolve的时候,就把数组中存放的回调函数依次执行
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if(this.status===PENDIND) {
this.reason = reason;
this.status = REJECTED;
// 同理,当调用到reject的时候,就把数组中存放的回调函数依次执行
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onfulfilled, onrejected) {
if(this.status===FULFILLED) {
onfulfilled(this.value);
}
if(this.status===REJECTED) {
onrejected(this.reason);
}
// 当存在异步的时候,先将then中的回调函数存入到设定的数组中
if(this.status===PENDIND) {
this.onResolvedCallbacks.push(() => {
// todo....
onfulfilled(this.value);
})
this.onRejectedCallbacks.push(() => {
// todo....
onrejected(this.value);
})
}
}
}
module.exports = Promise;
再执行之前的例子,可以看出也输出三次成功的结果。所以总结一下,就是promise
同步的话,直接执行,异步的话,发布订阅。
2. 进阶版
上面已经实现了一个简单的promise
结构,下面要考虑到promise
的一个特殊应用,then
方法的链式调用,可以解决传统异步“回调地狱”的问题。then
的链式调用大致特点如下:
then
的成功;then
中失败的方法;promise
,那么这个promise
会执行,并且会采用他的状态;下面来看具体代码的实现:
const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
// promise的处理函数
const resolvePromise = (promise2, x, resolve, reject) => {
// 处理x的类型,来决定调用resolve还是reject
// promise2自己等待自己完成,直接报错
if(promise2 === x) {
return reject(new TypeError(`Chaining cycle detected for promise #`));
}
// 判断x是不是一个普通值,暂先认为x是一个Promise来处理
if((typeof x === 'object' && x !== null) || typeof x === 'function') {
// x可能是一个promise,用promise的特点then方法来判断
// 取then的过程中可能会抛出异常,报错就好了
try {
let then = x.then;
if(typeof then === 'function') {
// 即说明x是个promise,就采用这个promise的结果
then.call(x, y => {
resolve(y);
}, r => {
reject(r);
})
} else {
// 常量直接抛出去即可
resolve(x);
}
} catch(e) {
reject(e);
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
this.value = undefined,
this.reason = undefined;
this.status = PENDIND;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if(this.status===PENDIND) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if(this.status===PENDIND) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onfulfilled, onrejected) {
let promise2 = new Promise((resolve, reject) => {
if(this.status===FULFILLED) {
// 为了能够取到promise2,将其放在异步的setTimeout中
// 当前onfulfilled和onrejected不能在当前的上下文中执行,为了确保promise2的存在
setTimeout(() => {
try {
let x = onfulfilled(this.value);
// 这里还需要判断x是不是一个promise,如果是一个promise,就采用这个promise执行的结果
// 根据Promise A+规范这里用一个函数统一处理
// 这里要用x来判断promise2的成功还是失败,所以传入以下参数
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.status===REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.status===PENDIND) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
})
return promise2;
}
}
module.exports = Promise;
下面来测试一下:
let Promise = require('./promise');
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功了");
}, 1000)
})
let promise2 = p.then(data => {
// 如果promise2和x引用了同一个对象,相当于自己等自己完成,会报一个类型错误
// return promise2;
return [1,2,34];
// 如果返回的x是一个promise
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("又成功了");
}, 1000)
})
})
promise2.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
打印的结果也是理想的,上面的实例包含了多中情况,包括返回的是一个自身的promise
会报错then
中返回的是一个promise
实例等等,都符合promise A+
规范。
下面考虑一种特殊的情况,那就是promise
成功之后,再resolve
一个新的promise
:
// let Promise = require('./promise');
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功了");
}, 1000)
})
let promise2 = p.then(data => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve("又成功了");
}, 1000)
}));
}, 1000)
})
})
promise2.then(data => {
console.log("success", data);
}, err => {
console.log("error", err);
})
标准的Promise
返回的结果是成功执行resolve
中promise
的结果:
success 又成功了
而显然我们自己实现的promise
还做不到这一点,需要在原本实现的promise
基础上对resolvePromise
方法进行改进,就是要考虑到该方法中的y
有可能还是个promise
,所以再调用resolvePromise
对y
进行判断并处理就可以了,其实就是不停地递归的一个过程,知道返回的y
是个常量,就输出:
const resolvePromise = (promise2, x, resolve, reject) => {
if(promise2 === x) {
return reject(new TypeError(`Chaining cycle detected for promise #`));
}
if((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then;
if(typeof then === 'function') {
then.call(x, y => {
// y有可能还是个promise,所以需要继续判断
resolvePromise(promise2, y, resolve, reject);
}, r => {
reject(r);
})
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
} else {
resolve(x);
}
}
再尝试之前的例子,可以输出promise
都执行完之后的结果了。
在这个基础上再考虑一种情况,如果返回的是一个新的和之前的promise
没有任何关系的promise
,那我们实现的promise
就不能保证他成功之后不会继续调用失败,这里需要加入判断条件called
,调用过成功就不能调用失败了,防止重复调用:
const resolvePromise = (promise2, x, resolve, reject) => {
if(promise2 === x) {
return reject(new TypeError(`Chaining cycle detected for promise #`));
}
if((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called;
try {
let then = x.then;
// 默认没有调用成功和失败
if(typeof then === 'function') {
then.call(x, y => {
// 一旦成功,就让called为true
// 防止多次调用
if(called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => {
if(called) return;
called = true;
reject(r);
})
} else {
resolve(x);
}
} catch(e) {
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
以下是最终版的Promise实现:
const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
if(promise2 === x) {
return reject(new TypeError(`Chaining cycle detected for promise #`));
}
if((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called;
try {
let then = x.then;
if(typeof then === 'function') {
then.call(x, y => {
if(called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => {
if(called) return;
called = true;
reject(r);
})
} else {
resolve(x);
}
} catch(e) {
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
this.value = undefined,
this.reason = undefined;
this.status = PENDIND;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if(this.status===PENDIND) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if(this.status===PENDIND) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onfulfilled, onrejected) {
let promise2 = new Promise((resolve, reject) => {
if(this.status===FULFILLED) {
setTimeout(() => {
try {
let x = onfulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.status===REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.status===PENDIND) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
})
return promise2;
}
}
module.exports = Promise;