目标:实现1.Promise的异步机制,2.then的链式调用
1.resolve和reject调用(同步)
// 等待中
const PENDING = 'pending';
// 成功
const RESOLVE = 'resolve';
// 失败
const REJECT = 'reject';
class MyPromise {
constructor(execute) {
execute(this.resolve, this.reject);
}
// 当前状态
status = PENDING;
// 成功的数据
value = null;
// 失败数据
fail = null;
resolve = value => {
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVE;
}
};
reject = value => {
if (this.status === PENDING) {
this.fail = value;
this.status = REJECT;
}
};
then(onFuResolv, onFuReject) {
// 容错处理,使then里面的参数可以选
onFuResolv = typeof onFuResolv === 'function' ? onFuResolv : value => value;
onFuReject = typeof onFuReject === 'function' ? onFuReject : value => new Error(this.fail);
if (this.status === RESOLVE) {
onFuResolv(this.value);
} else if (this.status === REJECT) {
onFuReject(this.fail);
}
}
}
module.exports = MyPromise;
// 测试
const p1 = new MyPromise((resolve, reject) => {
resolve('成功');
reject('失败');
});
p1.then(
res => {
console.log('res', res);
},
e => {
console.log('e', e);
}
);
// res 成功
上面只支持同步调用,如果new MyPromise 里面有异步任务就不会有返回值:如
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 0);
});
p1.then(res => {
console.log('res', res); // 不会有返回结构
})
2.实现在 new MyPromise时如果异步调用 resolve 或 reject在then里面能拿到结果
因为在 then 里面 判断 status 是 PENDING 状态时,将未执行的方法添加到了数组里面,在 resolve 或 reject 执行时会循环调用 成功 或 失败 的回调函数,这就实现了另一个功能,多次 new MyPromise,不会产生冲突
class MyPromise {
constructor(execute) {
execute(this.resolve, this.reject);
}
// 当前状态
status = PENDING;
// 成功的数据
value = null;
// 失败数据
fail = null;
// 新增
// 成功回调的集合
onFuResolvCallback = [];
// 失败回调的集合
onFuRejectCallback = [];
resolve = value => {
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVE;
// 新增
// 当 resolve 异步执行时 onFuResolvCallback有未执行的回调时
while (this.onFuResolvCallback.length) {
this.onFuResolvCallback.shift()(this.value);
}
}
};
reject = value => {
if (this.status === PENDING) {
this.fail = value;
this.status = REJECT;
// 新增
// 当 reject 异步执行时 onFuResolvCallback有未执行的回调时
while (this.onFuRejectCallback.length) {
this.onFuRejectCallback.shift()(this.fail);
}
}
};
then(onFuResolv, onFuReject) {
// 容错处理,使then里面的参数可以选
onFuResolv = typeof onFuResolv === 'function' ? onFuResolv : value => value;
onFuReject = typeof onFuReject === 'function' ? onFuReject : value => new Error(this.fail);
if (this.status === RESOLVE) {
onFuResolv(this.value);
} else if (this.status === REJECT) {
onFuReject(this.fail);
// 新增
} else if (this.status === PENDING) {
this.onFuResolvCallback.push(onFuResolv);
this.onFuRejectCallback.push(onFuReject);
}
}
}
// 测试
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
// reject('失败');
}, 50);
});
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
// resolve('成功');
reject('失败');
}, 1000);
});
p2.then(res => {
console.log('res', res);
});
p1.then(
res => {},
e => {
console.log('e', e);
}
);
// e 失败
// res 成功
3.使其支持链式调用及嵌套Promise
通过在then里面返回一个 MyPromise2 使其支持链式调用
在 RESOLVE,PENDING,PENDING三个状态里面添加对应的微任务,使其支持在then里面可以嵌套异步任务
通过 thenRetHandle 方法处理then返回的数据(重要)
then(onFuResolv, onFuReject) {
// 容错处理,使then里面的参数可以选
onFuResolv = typeof onFuResolv === 'function' ? onFuResolv : value => value;
onFuReject = typeof onFuReject === 'function' ? onFuReject : value => new Error(this.fail);
// 返回一个新的 MyPromise 使其可以链式调用 then
const MyPromise2 = new MyPromise((resolve, reject) => {
// 成功的微任务
const resolveMicrotask = () => {
queueMicrotask(() => {
const x = onFuResolv(this.value);
thenRetHandle(MyPromise2, x, resolve, reject);
});
};
// 失败的微任务
const rejectMicrotask = () => {
const x = onFuReject(this.fail);
thenRetHandle(MyPromise2, x, resolve, reject);
};
if (this.status === RESOLVE) {
resolveMicrotask();
} else if (this.status === REJECT) {
rejectMicrotask();
} else if (this.status === PENDING) {
queueMicrotask(() => {
this.onFuResolvCallback.push(resolveMicrotask);
this.onFuRejectCallback.push(rejectMicrotask);
});
}
});
return MyPromise2;
}
// 每次 then 时返回值的处理(重要函数,用来处理then返回的数据)
function thenRetHandle(MyPromise2, x, resolve, reject) {
// 防止重复调用
if (x === MyPromise2) {
return reject(new TypeError('重复调用'));
}
if ((typeof x === 'object' && typeof x !== null) || typeof x === 'function') {
let then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
// 递归调用,y 可能又是 promise
thenRetHandle(MyPromise2, y, resolve, reject);
},
r => {
reject(r);
}
);
} else {
resolve(x);
}
} else {
// 不是对象或不是函数时直接返回
resolve(x);
}
}
// 测试结果
const p1 = new MyPromise((resolve, reject) => {
resolve('成功');
});
let p2 = p1.then(res => {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('哈哈哈哈');
}, 0);
})
);
}, 0);
});
});
let p3 = p2
.then(res => {
console.log('0', res);
return res;
})
.then(res => {
return new Promise(resolve => {
setTimeout(() => {
console.log('1', res);
resolve(res);
}, 500);
});
});
p3.then(res => {
console.log('p3', res);
});
// 0 哈哈哈哈
// 1 哈哈哈哈
// p1 哈哈哈哈
4.添加错误处理及完整代码
// 等待中
const PENDING = 'pending';
// 成功
const RESOLVE = 'resolve';
// 失败
const REJECT = 'reject';
class MyPromise {
constructor(execute) {
try {
execute(this.resolve, this.reject);
} catch (e) {
reject(e);
}
}
// 当前状态
status = PENDING;
// 成功的数据
value = null;
// 失败数据
fail = null;
// 成功回调的集合
onFuResolvCallback = [];
// 失败回调的集合
onFuRejectCallback = [];
resolve = value => {
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVE;
// 当 resolve 异步执行时 onFuResolvCallback有未执行的回调时
while (this.onFuResolvCallback.length) {
this.onFuResolvCallback.shift()(this.value);
}
}
};
reject = value => {
if (this.status === PENDING) {
this.fail = value;
this.status = REJECT;
// 当 reject 异步执行时 onFuResolvCallback有未执行的回调时
while (this.onFuRejectCallback.length) {
this.onFuRejectCallback.shift()(this.fail);
}
}
};
then(onFuResolv, onFuReject) {
// 容错处理,使then里面的参数可以选
onFuResolv = typeof onFuResolv === 'function' ? onFuResolv : value => value;
onFuReject = typeof onFuReject === 'function' ? onFuReject : value => new Error(this.fail);
// 返回一个新的 MyPromise 使其可以链式调用 then
const MyPromise2 = new MyPromise((resolve, reject) => {
// 成功的微任务
const resolveMicrotask = () => {
queueMicrotask(() => {
try {
const x = onFuResolv(this.value);
thenRetHandle(MyPromise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
};
// 失败的微任务
const rejectMicrotask = () => {
try {
const x = onFuReject(this.fail);
thenRetHandle(MyPromise2, x, resolve, reject);
} catch (e) {
reject(e);
}
};
if (this.status === RESOLVE) {
resolveMicrotask();
} else if (this.status === REJECT) {
rejectMicrotask();
} else if (this.status === PENDING) {
queueMicrotask(() => {
this.onFuResolvCallback.push(resolveMicrotask);
this.onFuRejectCallback.push(rejectMicrotask);
});
}
});
return MyPromise2;
}
}
// 每次 then 时返回值的处理
function thenRetHandle(MyPromise2, x, resolve, reject) {
// 防止重复调用
if (x === MyPromise2) {
return reject(new TypeError('重复调用'));
}
if ((typeof x === 'object' && typeof x !== null) || typeof x === 'function') {
let then;
try {
then = x.then;
} catch (e) {
reject(e);
}
if (typeof then === 'function') {
try {
then.call(
x,
y => {
// 递归调用,y 可能又是 promise
thenRetHandle(MyPromise2, y, resolve, reject);
},
r => {
reject(r);
}
);
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
} else {
// 不是对象或不是函数时直接返回
resolve(x);
}
}
5.新增静态 all 方法
class MyPromise {
constructor(execute) {
try {
execute(this.resolve, this.reject);
} catch (e) {
reject(e);
}
}
// 当前状态
status = PENDING;
// 成功的数据
value = null;
// 失败数据
fail = null;
// 成功回调的集合
onFuResolvCallback = [];
// 失败回调的集合
onFuRejectCallback = [];
resolve = value => {
.........
};
reject = value => {
.........
};
then(onFuResolv, onFuReject) {
.........
}
// 新增
static all(array) {
// 返回的数据
let returns = [];
// 记数器 (用作记录是否所有任务都执行完成(异步))
let index = 0;
return new MyPromise((resolve, reject) => {
// 错误处理
if (!Array.isArray(array)) return reject(new TypeError('类型错误,请传入数组'));
else if (array.length === 0) return resolve(array);
// 添加返回值
const addRet = (key, value) => {
returns[key] = value;
index++;
if (index === array.length) resolve(returns);
};
for (let i = 0; i < array.length; i++) {
try {
let current = array[i];
if (current instanceof MyPromise) {
current.then(
value => {
addRet(i, value);
},
e => {
reject(e);
}
);
} else {
addRet(i, current);
}
} catch (e) {
reject(e);
}
}
});
}
}
// 测试
let a1 = () => {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
// resolve('a3');
resolve(new Error('错误'));
}, 2000);
});
};
let a2 = () => {
return new MyPromise((resolve, reject) => {
resolve('a4');
});
};
MyPromise.all(['a1', 'a2', a1(), a2(), 'a5']).then(
res => {
console.log('res', res);
},
e => console.log('/', e)
);
// ["a1", "a2", Error: 错误 at eval...., "a4","a5"]
all的使用小技巧
// 注意:这里只能使用可迭代的map之类的方法,不确定是否描述的正常,但是forEach肯定会报错,map不会报错
Promise.all(arr.map(item => {
// 在这里发送请求
// 如果其中一项失败了,你并不想让其中断,可以通过 catch 捕获或者把错误的值赋值默认值之类的操作
item.catch(e => '')
})).then(res => {
// res 是最后返回的结果,是一个数组
})