个人感悟
经过一个星期业务的摧残,就算学习完了拉勾的课程,也没有办法动手,趁着周六总算可以把手写Promise的心愿完成了,所有学习内容都在这回代码注释里面了.前面一大段是总的开发过程 然后在代码里注释相应代码的作用.终于掌握了promise,拉勾这个课程俺还是觉得蛮好的解决了面试有关Promise的痛点,以后这方面的面试题也都不用担心了。
在撸代码前先把异步编程相关知识点补上。
异步编程
javascript是单线程,是为了防止对对dom进行误操作。但浏览器是多线程的,它包含:GUI线程、js引擎线程、定时触发器线程、事件触发线程、异步http请求线程。为了确保UI渲染准确,UI渲染线程与JavaScript引擎为互斥的关系。
javascript分位同步任务和异步任务,异步模式 不会去等待这个任务的结束才开始下一个任务,都是开启过后就立即往后执行下一个任务。耗时函数的后续逻辑会通过回调函数的方式定义。
事件循环
异步任务又分为宏任务和微任务,每一次执行栈清空后,先将微任务队列的回调事件推入任务栈(微任务重新产生的微任务也在这次循环中执行),等任务栈清空后再推入一个宏任务。
微任务:Promise MutationObserver queueMicroTask
宏任务:setTimeout setInterval script
手写Promise
//Promise是一个类
//Promise有状态,状态有三种类型 pending, fulfilled, rejected
//promise有两个基础方法 resolve 和 reject
//resolve和reject都是会改变Promise状态
//resolve会从pending->fulfilled,reject会从pending->rejected,状态一旦改变就不能再次改变
//Promise的构造函数接收一个执行器,该执行器接收两个参数resolve、reject,实际执行的Promise类的方法
//Promise有一个then方法,接受两个回调,一个成功回调,一个失败回调,根据状态判断执行哪个回调,回调参数是可选的(也就是可传可不传)
//resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
//then返回的是一个Promise(链式调用最关键的点),而且要延迟执行,这个Promise的状态就是在这次then执行里面改变的
//兼容Promise里面是延迟执行的case,把成功和失败函数缓存起来,因为一个promise可以有多个then所以用数组缓存
//当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
//then返回的Promise对象状态是由回调函数结果决定,如果回调函数返回一个普通对象,then则返回一个resolve状态,值为返回对象的Promise对象,如果返回一个Promise对象则将这个Promise对象的状态和值传递给then返回的Promise,且这个Promise对象不可以是自己
//为了实现上一步所以需要写一个方法resolvePromise做处理
//执行器步骤都需要错误处理
//then到这一步就结束了
//Promise.all()的实现,因为能通过类直接调用说明是静态方法,需要用static关键字声明
//Promise.all()接收一个数组(可以是普通对象也可以是Promise对象)
//当所有参数数组里所有Promise对象执行完成后返回一个fullfilled状态的Promise,值为Promise返回的值的数组,只要参数数组里有一个抛出一场则返回一个rejected的Promise对象
//在Promise.all里面会把参数数组里的Promise对象和普通对象分开处理,如果可以用Promise.resolve另说
//Promise.all的核心思想就是返回一个Promise对象,在该对象的执行器中遍历参数数组并执行,把结果放入到结果数组里,同时需要一个计数器判断是否完成
//race方法差不多
//Promise.resolve如果参数是Promise对象就返回参数,如果不是Promise对象就返回一个,也是个静态方法
//finally方法,接受一个函数参数,无论Promise对象状态如何都会执行这个函数参数,且返回当前Promise值,这个时候就可以借助then方法,而且当这个回调函数返回一个Promise时要等这个返回的Promise执行完才继续执行后续的then
//catch方法,接受一个函数参数返回一个Promise值,该Promise回调返回值决定,也借助then方法
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
//Promise是一个类
class LrbPromise {
status = PENDING; //Promise有状态,状态有三种类型 pending, fulfilled, rejected
//resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
value = void 0;
reason = void 0;
successCallbackList = [];
failCallbackList = [];
constructor(executor) { //Promise的构造函数接收一个执行器,该执行器接收两个参数resolve、reject,实际执行的Promise类的方法
try { //执行器步骤都需要错误处理
executor(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = (value) => { //注意这里用尖头函数保留this
if (this.status !== PENDING) return; //状态一旦改变就不能再次改变
this.status = FULFILLED; //resolve会改变Promise状态,从pending->fulfilled
this.value = value; //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
while(this.successCallbackList.length) { //当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
this.successCallbackList.shift()()
}
}
reject = (reason) => { //注意这里用尖头函数保留this
if (this.status !== PENDING) return; //状态一旦改变就不能再次改变
this.status = REJECTED; //reject会改变Promise状态,从pending->rejected
this.reason = reason; //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
while(this.failCallbackList.length) { //当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
this.failCallbackList.shift()()
}
}
//Promise有一个then方法,接受两个回调,一个成功回调,一个失败回调,根据状态判断执行哪个回调,回调参数是可选的(也就是可传可不传)
then (successCallback, failCallback) {
successCallback = successCallback ? successCallback : value => value; //回调参数是可选的(也就是可传可不传)
failCallback = failCallback ? failCallback : e => { throw(e) }
const promise2 = new LrbPromise((resolve, reject) => { // 注意这里用尖头函数保留this,then返回的Promise对象状态是由回调函数结果决定
if (this.status === FULFILLED) { //如果是fulfilled状态执行成功回调
setTimeout(() => { //注意这里用尖头函数保留this
try { //执行器步骤都需要错误处理
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
} else if (this.status === REJECTED) { //如果是rejected状态执行成功回调
setTimeout(() => {
try { //执行器步骤都需要错误处理
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
} else {
//兼容Promise里面是延迟执行的case,把成功和失败函数缓存起来,因为一个promise可以有多个then所以用数组缓存
this.successCallbackList.push(() => {
setTimeout(() => { //注意这里用尖头函数保留this
try { //执行器步骤都需要错误处理
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0)
});
this.failCallbackList.push(() => {
setTimeout(() => { //注意这里用尖头函数保留this
try { //执行器步骤都需要错误处理
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0)
});
// this.failCallbackList.push( () => {
// setTimeout(() => {
// try {
// let x = failCallback(this.reason);
// resolvePromise (promise2, x, resolve, reject);
// } catch (e) {
// reject(e)
// }
// }, 0)
// });
}
})
return promise2;
}
//Promise.resolve如果参数是Promise对象就返回参数,如果不是Promise对象就返回一个,也是个静态方法
static resolve (value) {
return value instanceof LrbPromise ? value : new LrbPromise(resolve => resolve(x));
}
//Promise.all()的实现,因为能通过类直接调用说明是静态方法,需要用static关键字声明,接受一个参数数组
//Promise.all的核心思想就是返回一个Promise对象,在该对象的执行器中遍历参数数组并执行,把结果放入到结果数组里,同时需要一个计数器判断是否完成
static all (arr) {
return new LrbPromise((resolve, reject) => {
if (!Array.isArray(arr)) return reject(new TypeError('请传入数组'));
const result = [];
let counter = 0;
//当所有参数数组里所有Promise对象执行完成后返回一个fullfilled状态的Promise,值为Promise返回的值的数组,只要参数数组里有一个抛出一场则返回一个rejected的Promise对象
function addValue (i, val) {
result[i] = val;
counter++;
if (counter === arr.length) {
resolve(result)
}
}
for (let i = 0; i < arr.length; i++) {
//在Promise.all里面会把参数数组里的Promise对象和普通对象分开处理,如果可以用Promise.resolve另说
if (arr[i] instanceof LrbPromise) {
arr[i].then(res => {
addValue(i, res);
}, reject)
} else {
addValue(i, arr[i]);
}
}
//如果存在Promise.resolve
// for (let i = 0; i < arr.length; i++) {
// LrbPromise.resolve(arr[i]).then(res => {
// result[i] = res;
// counter++;
// if (counter === arr.length) {
// resolve(result)
// }
// }, reject)
// }
})
}
//finally方法,接受一个函数参数,无论Promise对象状态如何都会执行这个函数参数,且返回当前Promise值,这个时候就可以借助then方法,而且当这个回调函数返回一个Promise时要等这个返回的Promise执行完才继续执行后续的then
finally (callback) {
return this.then((value) => {
return LrbPromise.resolve(callback()).then(() => value)
}, (reason) => {
return LrbPromise.resolve(callback()).then(() => { throw reason })
})
}
//catch方法,接受一个函数参数返回一个Promise值,该Promise回调返回值决定,也借助then方法
catch (callback) {
return this.then(void 0, callback)
}
}
//then返回的Promise对象状态是由回调函数结果决定,如果回调函数返回一个普通对象,then则返回一个resolve状态,值为返回对象的Promise对象,如果返回一个Promise对象则将这个Promise对象的状态和值传递给then返回的Promise,且这个Promise对象不可以是自己
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('不可以返回自己')); //这里可以试试直接throw
}
if (x instanceof LrbPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
function getPromise () {
return new LrbPromise((resolve, reject) => {
// resolve(1)
// setTimeout(() => {
// resolve('成功')
// });
// resolve('成功')
reject('失败')
})
}
const p2 = getPromise();
p2.then()
.then(res => console.log('成功:', res), reason => console.log('失败:', reason))
// const p1 = new LrbPromise((resolve, reject) => {
// // resolve(1)
// // reject(1)
// }).then((value) => {
// console.log(value);
// }, (reason) => {
// console.log(reason);
// })
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!