1、引入Promise
- 字面意义"承诺",将来的某个时间进行兑现
- 主要解决地域回调问题
- Promise是异步的一种解决方案
- Promise是一个构造函数,有属性和方法;通过new实例化使用
2、Promise相关特点
3、 源码解析
步骤分析
- 1、new MyPromise() 时自动触发executor函数(同步执行)
- 2、调用then方法时,收集成功或失败函数(前提executor内异步执行)
- 3、手动调用resolve或reject时,异步循环执行上一步收集的函数
- 4、对上一步每次函数的执行结果放到resolvePromise函数中做处理
源码实现
const PENDING = "PENDING",
FULFILLED = "FULFILLED",
REJECTED = "REJECTED";
class MyPromise {
//executor
///1、自执行函数,new MyPromise时立刻执行
///2、包含两个参数:resolve和reject函数
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 分别盛放所有的成功或失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 不放到原型上:resolve和reject
// 由于每次new的时候都需要创建自己的resolve和reject方法
const resolve = (value) => {
if (this.status == PENDING) {//状态只能被修改一次
this.value = value;
this.status = FULFILLED;
//发布:迭代执行成功函数
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (value) => {
if (this.status == PENDING) {//状态只能被修改一次
this.reason = value;
this.status = REJECTED;
//发布:迭代执行失败函数
this.onRejectedCallbacks.forEach(fn => fn());
}
}
// 实例化时:executor函数内异常捕获处理 - 例如:throw new Error()
try {
// executor:此方法同步执行
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
// 每个then都返回一个新的promie对象,进行链式调用
then(onFulfilled, onRejected) {
// 解决穿透问题:如果onFulfilled或onRejected为空,默认给一个函数
// 例如:promise2.then().then().then((resolve,reject)=>{...})
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };
// 创建一个promise对象作为返回值
let promise2 = new MyPromise((resolve, reject) => {
// 以下代码使用setTimeout包裹:
/// 1、使用setTimeout(宏任务)的处理方式,确保可以取到当前promise2的对象
/// 2、源码中用的是微任务的处理方式
/// 3、保障了多次then的链式调用,等待上次执行结束后才能执行下次( 事件循环机制 )
// 下列代码res的值作为resolve和reject返回值
/// 1、可能为普通值
/// 2、可能为primise对象
/// 3、使用resolvePromise方法进行处理
// 同步代码执行executor时,且状态为FULFILLED
if (this.status == FULFILLED) {
setTimeout(() => {
// 处理结果中抛出异常的情况,如:throw new Error()
try {
let res = onFulfilled(this.value)
// 处理返回值的函数
resolvePromise(promise2, res, resolve, reject)
} catch (error) {
reject(error);
}
}, 0);//具体延迟执行的时间>=4ms;
}
// 同步代码执行executor时,且状态为REJECTED
if (this.status == REJECTED) {
setTimeout(() => {
try {
let res = onRejected(this.reason)
resolvePromise(promise2, res, resolve, reject)
} catch (error) {
reject(error);
}
}, 0);
}
// 异步代码执行executor时:收集所有的成功或失败的回调函数
if (this.status == PENDING) {
// 订阅:收集成功函数
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let res = onFulfilled(this.value)
resolvePromise(promise2, res, resolve, reject)
} catch (error) {
reject(error);
}
}, 0);
})
// 订阅:收集失败函数
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let res = onRejected(this.reason)
resolvePromise(promise2, res, resolve, reject)
} catch (error) {
reject(error);
}
}, 0);
})
}
})
return promise2;
}
catch(errorCallback) {
return this.then(null, errorCallback)
}
resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value);
})
}
reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
})
}
//所有实例执行成功才算成功
all(promiseArray) {
if (!Array.isArray(promiseArray)) {
throw new TypeError(" The arguments should be an array!")
}
return new MyPromise((resolve, reject) => {
try {
const resultArray = [];
const length = promiseArray.length;
// 有一个实例失败直会接执行reject方法;
//否则等待所有实例执行完执行resolve方法
for (let i = 0; i < length; i++) {
promiseArray[i].then(data => {
resultArray.push(data);
if (resultArray.length === length) {
resolve(resultArray)
}
}, reject)
}
}
catch (e) {
reject(e)
}
})
}
// 返回第一个改变状态的结果,无论成功的还是失败的
race(promiseArray) {
if (!Array.isArray(promiseArray)) {
throw new TypeError(" The arguments should be an array!")
}
return new MyPromise((resolve, reject) => {
try {
const length = promiseArray.length;
for (let i = 0; i < length; i++) {
promiseArray[i].then(resolve, reject);
}
}
catch (e) {
reject(e)
}
})
}
}
// 处理then中成功函数或失败函数的返回值
function resolvePromise(promise2, res, resolve, reject) {
// 如果promise2等于res,抛出异常
// 例如:let promise1 = promise.then((value) => { return promise1; })
if (promise2 == res) {
return reject(new TypeError('Changing cycle detected for promise #<MyPromise>'))
}
// 如果res是一个object或function时
if ((typeof res === "object" && res !== null) || (typeof res === "function")) {
//保证res.then取值的时候出现异常
try {
let then = res.then;//判断是否为 promise 对象
//判断回调函数是否被多次调用过:例如 - resolve();resolve();连续多次调用
let called = false;
//为promise对象
if (typeof then === "function") {
then.call(res, (y) => {
// 避免重复调用
if (called) return;
called = true;
// 如果Y为Promise对象,递归调用
// 如果Y为普通值,相当于下次直接调用 - resolve()
resolvePromise(promise2, y, resolve, reject)
}, (r) => {
// 避免重复调用
if (called) return;
called = true;
reject(r)
})
}
else {
resolve(res);
}
} catch (error) {
// 避免重复调用
if (called) return;
called = true;
reject(error)
}
}
//res为普通值
else {
resolve(res);
}
}
module.exports = MyPromise;
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
- 找不到素材资源介绍文章里的示例图片?
- 对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
- 模板不会安装或需要功能定制以及二次开发?
- 请QQ联系我们
发表评论
还没有评论,快来抢沙发吧!