先看下Promise的基本使用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 0);
});
p.then(
(data) => {
console.log(data);
},
(error) => {
console.log(error);
}
);
根据我们平时使用的经验和Promise/A+规范,可以知道
- 一个Promise有三个状态
- 有两个更改状态的函数resolve和reject
- 成功或失败的值
- 成功或失败的回调函数(这里就可以使用发布订阅的模式)
- then可以链式调用
- Promise的参数就是一个函数并且同步执行
- 规范里还提到then的返回值不同会有不同的处理方式
首先声明一个类,把一开始的过程用代码体现以下
const PENDING = "PENGDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class MyPromise {
constructor(exec) {
//状态
this.status = PENDING;
//resolve()传得值
this.value = undefined;
//reject()传得值
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
};
try {
exec(resolve, reject);
} catch (error) {
reject(error);
}
}
}
一开始仅仅是执行了exec用户传入的函数初始化状态和声明两个resolve函数和reject函数并改变Promise实例的状态,同时保证只有在状态是PENGDING时才可以更改状态
then方法
- 返回一个新的Promise实例
- 在不同的Promise实例状态下执行失败回调还是成功回调,或者在PENGING状态下把回调函数存储起来
//then方法返回一个新的promise
then(onFulfilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
//需要判断onFulfilled()的返回值是不是一个Promise还是普通值
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
//切片编程 AOP
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
- 上面的代码处理了用户的回调函数,没有转换Promise2的状态,所以要根据规范写个resolvePromise来处理promise2的状态
- 这里把onFulfilled 和onReject放进了存放回调函数的数组,我们要在一开始声明的 resolve函数里执行
this.onResolvedCallbacks.forEach((fn) => fn());
在reject里执行this.onRejectedCallbacks.forEach((fn) => fn());
resolvePromise
- 利用onFulfilled或者onReject的返回值x来判断是调用promise2的resolve还是reject
- 判断是不是thenable对象,如果是就递归调用直到返回值是普通值
- then需要绑定this值并且传递两个回调函数
function resolvePromise(promise2, x, resolve, reject) {
//如果x和promise2是同一个对象,就抛出类型错误
if (x === promise2) {
reject(new TypeError("类型错误"));
}
//判断是不是thenable对象 (有then函数属性并且是对象或者是函数)
if ((typeof x === "object" && x !== null) || typeof x === "function") {
//防止反复调用成功或者失败,只能调用一次
let called = false;
try {
let then = x.then;
if (typeof then === "function") {
//绑定this值,并传入两个回调函数作为参数
then.call(
x,
(y) => {
//递归调用resolvePromise直到返回值不是promise
if (called) {
return;
}
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) {
return;
}
called = true;
reject(r);
}
);
} else {
//{} {then:{}}
resolve(x);
}
} catch (error) {
if (called) {
return;
}
called = true;
reject(error);
}
} else {
resolve(x);
}
}
最后
感觉最重要的是
– 发布订阅处理用户传入的回调函数
– then方法如何支持链式调用
完整代码贴一下,还有一些其他的方法
const PENDING = "PENGDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
function resolvePromise(promise2, x, resolve, reject) {
//如果x和promise2是同一个对象,就抛出类型错误
if (x === promise2) {
reject(new TypeError("类型错误"));
}
//判断是不是thenable对象 (有then函数属性并且是对象或者是函数)
if ((typeof x === "object" && x !== null) || typeof x === "function") {
//防止反复调用成功或者失败,只能调用一次
let called = false;
try {
let then = x.then;
if (typeof then === "function") {
//绑定this值,并传入两个回调函数作为参数
then.call(
x,
(y) => {
//递归调用resolvePromise直到返回值不是promise
if (called) {
return;
}
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) {
return;
}
called = true;
reject(r);
}
);
} else {
//{} {then:{}}
resolve(x);
}
} catch (error) {
if (called) {
return;
}
called = true;
reject(error);
}
} else {
resolve(x);
}
}
class MyPromise {
constructor(exec) {
//状态
this.status = PENDING;
//resolve()传得值
this.value = undefined;
//reject()传得值
this.reason = undefined;
//利用发布订阅存放成功或失败的回调函数,在then方法里面
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
//resolve()参数传入的新的promise
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
exec(resolve, reject);
} catch (error) {
reject(error);
}
}
//then方法返回一个新的promise
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === "function"
? onRejected
: (error) => {
throw error;
};
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
//需要判断onFulfilled()的返回值是不是一个Promise还是普通值
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
//切片编程 AOP
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
catch(errorFn) {
return this.then(null, errorFn);
}
static all(promises) {
return new MyPromise((resolve, reject) => {
let result = [];
let times = 0;
const processSuccess = (index, value) => {
result[index] = value;
//计数器数和promises长度一致就调用resolve()
if (++times === promises.length) {
resolve(result);
}
};
for (let index = 0; index < promises.length; index++) {
const p = promises[index];
if (p && typeof p.then === "function") {
p.then((data) => {
processSuccess(index, data);
}, reject);
} else {
//普通值
processSuccess(index, data);
}
}
});
}
//谁快用谁
static race(promises) {
return new MyPromise((resolve, reject) => {
for (let index = 0; index < promises.length; index++) {
const p = promises[index];
if (p && typeof p.then === "function") {
p.then(resolve, reject); //一旦成功或失败就调用resolve或者reject,停止promise
} else {
resolve(p);
}
}
});
}
//图片加载失败,脚本加载超时
static wrap(p) {
let abort;
let p1 = new MyPromise((resolve, reject) => {
abort = reject;
});
let p2 = MyPromise.race([p, p1]);
p2.abort = abort;
return p2;
}
finally(cb) {
return this.then(
(data) => {
return MyPromise.resolve(cb()).then(() => data);
},
(error) => {
return MyPromise.resolve(cb()).then(() => {
throw error;
});
}
);
}
}