一、promise解决了哪些问题?
- 异步并发 待所有异步任务结束之后再执行我们的业务逻辑 。
- 回调嵌套
const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';class MyPromise {constructor(executor) {// 初始状态为pending等待状态this.status = PENDING;const resolve = (value) => {// 执行resolve状态变为成功态this.status = FULFILLED;};const reject = (reason) => {// 执行reject状态变为失败态this.status = REJECTED;};try {// 执行传入的回调函数executor(resolve, reject);} catch (e) {// 回调函数执行出错,也会执行rejectreject(e);}}}由于promise的状态一旦转成了成功态或者失败态,就不能再改变状态了,所以我们需要加个判断,只有当状态为pending等待态的时候,才能将状态改为成功或失败,所以当状态变为了成功,再去调用resolve的话,无法满足判断条件,就不会继续执行了 。
0.0.2版
const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';class MyPromise {constructor(executor) {// 初始状态为pending等待状态this.status = PENDING;const resolve = (value) => {// 执行resolve状态变为成功态if (this.status === PENDING) {this.status = FULFILLED;}};const reject = (reason) => {// 执行reject状态变为失败态if (this.status === PENDING) {this.status = REJECTED;}};try {// 执行传入的回调函数executor(resolve, reject);} catch (e) {// 回调函数执行出错,也会执行rejectreject(e);}}}这样调用的话,状态改变之后就不会再改了
let p = new MyPromise((resolve,reject) => {resolve('success')reject('success')})console.log('p: ', p);接下来我们实现一下promise实例的then方法,then方法的特点在上面有提到哦
0.0.3版
class MyPromise {constructor(executor) {// 赋值到this上是为了方便在then方法中调用// 初始状态为pending等待状态this.status = PENDING;// 成功回调的参数this.value = https://tazarkount.com/read/undefined;// 失败回调的参数this.reason = undefined;const resolve = (value) => {// 执行resolve状态变为成功态 保存用户传入的参数if (this.status === PENDING) {this.status = FULFILLED;this.value = value;}};const reject = (reason) => {// 执行reject状态变为失败态 保存用户传入的参数if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;}};try {// 执行传入的回调函数executor(resolve, reject);} catch (e) {// 回调函数执行出错,也会执行rejectreject(e);}}// 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调then(onFulfilled, onRejected) {// 根据当前的状态,执行对应的回调 。回调的参数为用户调用resolve或者reject传入的数据if (this.status === FULFILLED) {onFulfilled(this.value);}if (this.status === REJECTED) {onRejected(this.reason);}}}调用时
let p = new MyPromise((resolve,reject) => {resolve('success')})p.then((value) => {console.log(value);// success}, (reason) => {console.log(reason);}) 但是如果是在异步代码里面调用resolve方法,就不会执行成功回调了,例如:
let p = new MyPromise((resolve,reject) => {setTimeout(() => {resolve('success')}, 300);})p.then((value) => {console.log(value);// success}, (reason) => {console.log(reason);})这是因为代码执行到settimeout,会将回调放入宏任务队列,而不是立即执行,所以会先执行then方法,这个时候settimeout并未执行,所以resolve也未执行,此时的status = 'pending',then方法的两个回调都不会执行 。
因此,我们想要执行异步代码里的resolve或者reject函数的话,需要在执行then方法的时候,先把成功回调或失败回调先存起来,直到异步代码执行到resolve,再依次执行回调 。实现方式就是采用发布订阅模式,借助两个数组,一个是成功回调的数组,一个是失败回调的数组,在调用then时,若status = pending,就把回调存入对应的数组,之后在resolve(reject)函数中依次调用成功(失败)回调的数组的回调 。
【promise其实没那么难! 基于promise a+规范手写promise】0.0.4版
const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';class MyPromise {constructor(executor) {// 初始状态为pending等待状态this.status = PENDING;// 成功回调的参数this.value = https://tazarkount.com/read/undefined;// 失败回调的参数this.reason = undefined;this.onFulFilledCallbacks = []; // 存放成功的回调this.onRejectedCallbacks = []; // 存放失败的回调const resolve = (value) => {// 执行resolve状态变为成功态 保存用户传入的参数if (this.status === PENDING) {this.status = FULFILLED;this.value = value;this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调}};const reject = (reason) => {// 执行reject状态变为失败态 保存用户传入的参数if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调}};try {// 执行传入的回调函数executor(resolve, reject);} catch (e) {// 回调函数执行出错,也会执行rejectreject(e);}}// 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调then(onFulfilled, onRejected) {// 根据当前的状态,执行对应的回调 。回调的参数为用户调用resolve或者reject传入的数据if (this.status === FULFILLED) {onFulfilled(this.value);}if (this.status === REJECTED) {onRejected(this.reason);}// 状态为pending时 将用户传的回调存放到各自的队列中(若用户没有调用resolve或reject,则不会执行队列中的回调)if (this.status === PENDING) {this.onFulFilledCallbacks.push(() => {onFulfilled(this.value);});this.onRejectedCallbacks.push(() => {onRejected(this.reason);});}}}调用时
let p = new MyPromise((resolve,reject) => {setTimeout(() => {resolve('success')}, 300);})p.then((value) => {console.log(value, 1);// success 1}, (reason) => {console.log(reason);})p.then((value) => {console.log(value, 2);// success 2}, (reason) => {console.log(reason);})接下来我们来分析then链式调用的规则
(1)如果then方法中成功回调或失败回调返回的是一个非promise值,则将这个值传递给外层下一次then的成功回调参数
(2)如果then方法中成功回调或失败回调的执行报错了,则将错误信息传递给外层下一次then的失败回调参数
let pp = new Promise((resolve,reject) => {resolve(1)}).then((data) => {console.log('第一次 success', data);//第一次 success 1return 100;// throw new Error('error')}, (err) => {console.log('第一次 fail', err);}).then((data) => {console.log('第二次 success', data);//第二次 success 100}, (err) => {console.log('第二次 fail', err);})(3)如果then方法中成功回调返回的是一个promise值,
- 若在返回的这个promise内部调用了resolve函数,则将传入resolve的参数 传递给外层下一次then的成功回调参数;
- 若在返回的这个promise内部调用了reject函数,则将传入reject的参数,传递给外层下一次then的失败回调参数;
let pp2 = new Promise((resolve,reject) => {resolve(1)}).then((data) => {console.log('第一次 success', data);//第一次 success 1return new Promise((resolve, reject) => {// resolve(100);reject(200);});}, (err) => {console.log('第一次 fail', err);}).catch((data) => {console.log('第二次 fail', data);//第二次 fail 200});then的链式调用是如何实现的?
每次调用then,返回一个新的promise实例,这个实例上肯定也有then方法,就可以一直.then下去
0.0.5版
class MyPromise {constructor(executor) {// 省略了跟上一版一样的内容}then(onFulfilled, onRejected) {// 调用then的时候 会创建一个新的promise实例并返回let promise2 = new MyPromise((resolve, reject) => {// 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调if (this.status === FULFILLED) {// 需要拿到成功回调的返回值,传递给下一个thenlet x = onFulfilled(this.value);resolve(x);}if (this.status === REJECTED) {let x = onRejected(this.reason);resolve(x);}if (this.status === PENDING) {this.onFulFilledCallbacks.push(() => {let x = onFulfilled(this.value);resolve(x);});this.onRejectedCallbacks.push(() => {let x = onRejected(this.reason);resolve(x);});}});return promise2;}}这里涉及到MyPromise函数的递归执行,在调用函数时进行拆分,就好分析了
let pp = new Promise((resolve,reject) => {// resolve(1)reject(1)})// 调用pp的then会返回promise2let promise2 = pp.then(data =https://tazarkount.com/read/> {// 执行pp的成功回调 返回普通值x 会触发promise2的成功回调 相当于在这个promise2的内部调用resolve(x)// return 100;}, err => {// 执行pp的失败回调 也会触发promise2的成功回调return 100;})promise2.then((data) => {console.log(data);// 100}, (err) => {console.log(err);}) 上个版本只是实现了规则(1),处理的是成功或失败回调返回非promise值的情况,下面处理一下成功或失败的回调在执行时发生报错的情况,只需要加上try catch即可
0.0.6版
class MyPromise {constructor(executor) {// 此处省略}then(onFulfilled, onRejected) {// 调用then的时候 会创建一个新的promise实例并返回let promise2 = new MyPromise((resolve, reject) => {// 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调if (this.status === FULFILLED) {// 需要拿到成功回调的返回值,传递给下一个thentry {let x = onFulfilled(this.value);resolve(x);} catch (e) {reject(e);}}if (this.status === REJECTED) {try {let x = onRejected(this.reason);resolve(x);} catch (e) {reject(e);}}if (this.status === PENDING) {this.onFulFilledCallbacks.push(() => {try {let x = onFulfilled(this.value);resolve(x);} catch (e) {reject(e);}});this.onRejectedCallbacks.push(() => {try {let x = onRejected(this.reason);resolve(x);} catch (e) {reject(e);}});}});return promise2;}}接下来处理规则(3),也就是then方法中返回一个promise值的情况,需要给返回值注册成功和失败的回调,成功回调中执行promise2的resolve,失败回调中执行promise2的reject
规则(4)实际就是调用了then方法
0.0.7版(最终版)
const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';function resolvePromise(x, promise2, resolve, reject) {// then中成功或失败回调的返回值 不能和调用then方法返回的promise值相等if (x === promise2) {return reject(new TypeError('出错'));}if (x instanceof MyPromise) {try {let then = x.then;then.call(x,(y) => {// y代表返回的promise值内部调用resolve时传的参数 传给promise2的resolve方法并执行resolve(y);},(r) => {// r代表返回的promise值内部调用reject时传的参数 传给promise2的reject方法并执行reject(r);},);} catch (e) {reject(e);}} else {// 非promise值resolve(x);}}class MyPromise {constructor(executor) {this.status = PENDING;this.value = https://tazarkount.com/read/undefined;this.reason = undefined;this.onFulFilledCallbacks = []; // 存放成功的回调this.onRejectedCallbacks = []; // 存放失败的回调const resolve = (value) => {if (this.status === PENDING) {this.status = FULFILLED;this.value = value;this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调}};const reject = (reason) => {if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调}};try {executor(resolve, reject);} catch (e) {reject(e);}}then(onFulfilled, onRejected) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === FULFILLED) {// 使用settimeout是为了异步执行处理返回值的代码 否则直接使用promise2会报错setTimeout(() => {// 需要拿到成功回调的返回值,传递给下一个thentry {let x = onFulfilled(this.value);// 统一处理返回值resolvePromise(x, promise2, resolve, reject);} catch (e) {console.log(e);}}, 0);}if (this.status === REJECTED) {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(x, promise2, resolve, reject);} catch (e) {reject(e);}}, 0);}if (this.status === PENDING) {this.onFulFilledCallbacks.push(() => {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(x, promise2, resolve, reject);} catch (e) {reject(e);}}, 0);});this.onRejectedCallbacks.push(() => {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(x, promise2, resolve, reject);} catch (e) {reject(e);}}, 0);});}});return promise2;}
catch(onRejected) { return this.then(null, onRejected); }} 调用时
let pp = new MyPromise((resolve,reject) => {resolve(1)})// 调用pp的then会返回promise2let promise2 = pp.then(data =https://tazarkount.com/read/> {// 执行pp的成功回调 返回promise值x 会给x注册成功和失败回调,// 成功回调中执行promise2的成功回调,失败回调中执行promise2的失败回调return new MyPromise((res, rej) => {res(3000)})})// 若执行promise类型的返回值的resolve,就会执行第一个回调promise2.then((data) => {console.log(data);// 3000}, (err) => {console.log(err);}) 好啦!终于整理完成了!看完之后是不是发现promise其实也没那么难了!
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
