highlight: a11y-dark
theme: healer-readable
手写promise思路
先逐步分析,一步一步的写代码,最终有完整示例
1.promise是一个类,参数是一个函数执行器,且会立即执行。函数执行器接受两个参数,一个成功回调resolve,一个失败回调reject
class MyPromise {
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {}
reject(reason) {}
}
2.promise有3中状态pending,fulfilled,rejected,初始状态为pending,当promise的状态一经改变则无法再修改
// 在外部定义常量值
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';
......
constructor(executor) {
// 设置当前promise的状态值
this.status = PENDING;
executor(this.resolve.bind(this), this.reject.bind(this));
}
......
3.resolve/reject方法就是修改当前promise的状态的
......
resolve(value) {
// 如果当前状态不是初始状态,则不能被修改
if (this.status !== PENDING) return;
// 将当前状态修改为成功
this.status = FULFILLED;
}
......
reject(reason) {
// 如果当前状态不是初始状态,则不能被修改
if (this.status !== PENDING) return;
// 将当前状态修改为失败
this.status = REJECTED;
}
4.promise有一个then方法,它有两个参数,第一个参数是promise成功的回调,第二个参数是失败的回调
/* 因为successCallback和failCallback接受resolve和reject中的参数,
所需要记录成功的值和失败的值
*/
......
constructor(executor) {
......
// 记录成功时返回的值
this.value = undefined;
// 记录失败时返回的值
this.reason = undefined;
......
}
......
resolve(value) {
......
// 记录成功时返回的值
this.value = value;
}
......
reject(reason) {
......
// 记录失败时返回的值
this.reason = reason;
}
......
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
}
}
......
5.当函数执行器是同步的时候,promise的状态会马上被改变,所以then能接收到状态处理成功和失败,但是当函数执行器是异步的时候,promise的状态是等待状态,这时候就需要记录成功和失败的回调,等待异步执行器执行完毕后在调对应的回调函数
/* 所以我们需要两个变量来存贮then方法的成功回调和失败回调,当异步执行
器执行完毕后,resolve/reject分别执行存储的成功回调和失败回调
*/
......
constructor(executor) {
......
// 记录成功回调
this.successCallback = undefined;
// 记录失败回调
this.failCallback = undefined;
......
}
......
then(successCallback, failCallback) {
......
else if (this.status === PENDING) {
this.successCallback = successCallback;
this.failCallback = failCallback;
}
}
......
resolve(value) {
......
// 当执行器为异步时,执行器执行完毕,需要执行成功回调
this.successCallback(this.value);
}
......
reject(reason) {
......
// 当执行器为异步时,执行器执行完毕,需要执行失败回调
this.failCallback(this.reason);
}
......
6.一个promise可以同时执行多个then方法,且这些then方法的回调都会执行,当函数执行器为同步的时候,我们并不需要做处理,但是当函数执行器为异步时,我们需要把每个then方法的回调函数都保存起来,当函数执行器执行完毕的时候,再依次调用,所以,存储成功回调和失败回调是数组形式
// 存储成功回调和失败回调的变量都是数组
......
constructor(executor) {
......
// 记录成功回调
this.successCallback = [];
// 记录失败回调
this.failCallback = [];
......
}
......
then(successCallback, failCallback) {
......
else if (this.status === PENDING) {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
}
......
resolve(value) {
......
// 当执行器为异步时,执行器执行完毕,需要执行成功回调
while (this.successCallback.length)this.successCallback.shift()(this.value);
}
......
reject(reason) {
......
// 当执行器为异步时,执行器执行完毕,需要执行失败回调
while (this.failCallback.length) this.failCallback.shift()(this.reason);
}
......
7.promise可以链式调用then方法,意思就是then方法也要返回一个promise就可以链式调用then了,第二个then是第一个then中返回的promise对象的调用,这样API可以做到连续传递参数,且不需要回到地狱。如果在第一个then中的成功回调中返回一个普通值,那么下一个Then将接收到这个值,如果是个promise,那下一个then将是等待这个promise执行完毕,之后将值传递下去
then(successCallback, failCallback) {
/* 判断当前的successCallback/failCallback的返回值是否是异步的,
如果异步则需要等到异步完成
*/
const resolvePromise = (x, resolve, reject) => {
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = successCallback(this.value)
resolvePromise(x, resolve, reject);
} else if (this.status === REJECTED) {
const x = failCallback(this.reason);
resolvePromise(x, resolve, reject)
} else if (this.status === PENDING) {
this.successCallback.push(() => {
const x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
});
this.failCallback.push(() => {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
});
}
})
return promise2;
}
......
// 不需要传递this.value/this.reason参数了
resolve(value) {
......
// 当执行器为异步时,执行器执行完毕,需要执行成功回调
while (this.successCallback.length) this.successCallback.shift()();
}
......
reject(reason) {
......
// 当执行器为异步时,执行器执行完毕,需要执行失败回调
while (this.failCallback.length) this.failCallback.shift()();
}
......
8.在then方法中是不能返回自己的promose对象,不然会发生循环调用
/* 我们把then方法修改如下,这里想直接判断x和promise2是否相等做不到,因为这时候
还没有promise2,所以要加一个定时器,把回调变成异步的
*/
then(successCallback, failCallback) {
/* 判断当前的successCallback/failCallback的返回值是否是异步的,如果异步
则需要等到异步完成
*/
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
const x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject);
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
const x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
}, 0)
} else if (this.status === PENDING) {
this.successCallback.push(() => {
setTimeout(() => {
const x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
}, 0)
});
}
})
return promise2;
}
9.错误处理,函数执行器出错时,要执行reject回调,当then方法执行错误时,也要执行reject回调,这个reject是在下一次then方法的失败回调中抓取,采用tryCatch的方式抓取错误
// 首先抓取执行器的错误
constructor(executor) {
......
// 执行器,会立即执行,它接受两个参数,一个成功的回调,一个失败的回调
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
// 抓取then中的错误
then(successCallback, failCallback) {
/* 判断当前的successCallback/failCallback的返回值是否是异步的,如果异步
则需要等到异步完成
*/
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === PENDING) {
this.successCallback.push(() => {
setTimeout(() => {
try {
const x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
}
})
return promise2;
}
10.then方法的参数是可选参数,当resolve回调不传递时,其实是相当于传递了一个返回值的方法 ,then方法的两个参数得判断类型,当输入为函数的时候才会向下传递,如果是普通值则忽略
then(successCallback, failCallback) {
// 参数变成可选的,当不传递参数时相当于传递了类似的函数
// 这个地方要加上判断是不是函数类型,如果是普通值则忽略掉
if (successCallback) {
if (typeof successCallback !== 'function') {
successCallback = value => value;
}
} else {
successCallback = value => value;
}
if (failCallback) {
if (typeof failCallback !== 'function') {
reason => { throw reason }
}
} else {
reason => { throw reason }
}
......
}
11.promise.all是一个静态方法,可以执行多个promise,以及普通值
static all(array) {
const result = [];
let i = 0;
return new MyPromise((resolve, reject) => {
const addData = (key, value) => {
result[key] = value;
i++;
if (i === array.length) {
resolve(result);
}
}
array.forEach((item, index) => {
if (item instanceof MyPromise) {
item.then(value => addData(index, value), error => reject(error))
} else {
addData(index, item);
}
})
})
}
12.promise.resolve是一个静态方法,传递一个参数,如果传递的是一个普通参数,则将转换为promise返回,如果参数是promise,则直接返回
static resolve(item) {
if (item instanceof MyPromise) {
return item;
} else {
return new MyPromise(resolve => {
resolve(item)
})
}
}
13.promise.reject方法传递一个参数,返回一个promise,将参数向下传递进then方法的第二个回调函数中
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error)
})
}
14.finally方法,不管promise是成功开始失败,都会执行finally方法一次,finally后可以链式调用then方法拿到该promise的结果,如果finally方法返回了一个promise则后面的then需要等待
finally(callback) {
return this.then(value => {
const x = callback();
return MyPromise.resolve(x).then(() => value)
}, error => {
const x = callback();
return MyPromise.reject(x).then(undefined, () => error)
})
}
15.catch方法,抓取错误,他就相当于执行了then中的失败回调
catch(callback) {
this.then(undefined, callback);
}
promise完整示例
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';
/* 这个我用了平常的定义变量的方式,变量都在constructor中,这里就需要resolve和reject bind this了
也可以用声明式写在constructor外
*/
class MyPromise {
constructor(executor) {
// 设置当前promise的状态值
this.status = PENDING;
// 记录成功时返回的值
this.value = undefined;
// 记录失败时返回的值
this.reason = undefined;
// 记录成功回调
this.successCallback = [];
// 记录失败回调
this.failCallback = [];
// 执行器,会立即执行,它接受两个参数,一个成功的回调,一个失败的回调
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
// 如果当前状态不是初始状态,则不能被修改
if (this.status !== PENDING) return;
// 将当前状态修改为成功
this.status = FULFILLED;
// 记录成功时返回的值
this.value = value;
// 当执行器为异步时,执行器执行完毕,需要执行成功回调
while (this.successCallback.length) this.successCallback.shift()();
}
reject(reason) {
// 如果当前状态不是初始状态,则不能被修改
if (this.status !== PENDING) return;
// 将当前状态修改为失败
this.status = REJECTED;
// 记录失败时返回的值
this.reason = reason;
// 当执行器为异步时,执行器执行完毕,需要执行失败回调
while (this.failCallback.length) this.failCallback.shift()();
}
then(successCallback, failCallback) {
// 参数变成可选的,当不传递参数时相当于传递了类似的函数
// 这个地方要加上判断是不是函数类型,如果是普通值则忽略掉
if (successCallback) {
if (typeof successCallback !== 'function') {
successCallback = value => value;
}
} else {
successCallback = value => value;
}
if (failCallback) {
if (typeof failCallback !== 'function') {
reason => { throw reason }
}
} else {
reason => { throw reason }
}
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === PENDING) {
// 当执行器为异步的时候,需要记录回调函数,等待执行器执行完毕后在执行回调函数
this.successCallback.push(() => {
setTimeout(() => {
try {
const x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
}
})
return promise2;
}
finally(callback) {
return this.then(value => {
const x = callback();
return MyPromise.resolve(x).then(() => value)
}, error => {
const x = callback();
return MyPromise.reject(x).then(undefined, () => error)
})
}
catch(callback) {
this.then(undefined, callback);
}
static all(array) {
const result = [];
let i = 0;
return new MyPromise((resolve, reject) => {
const addData = (key, value) => {
result[key] = value;
i++;
if (i === array.length) {
resolve(result);
}
}
array.forEach((item, index) => {
if (item instanceof MyPromise) {
item.then(value => addData(index, value), error => reject(error))
} else {
addData(index, item);
}
})
})
}
static resolve(item) {
if (item instanceof MyPromise) {
return item;
} else {
return new MyPromise(resolve => {
resolve(item)
})
}
}
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error)
})
}
}
版权声明:本文为qq_35258995原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。