【手写】promise
大纲
前置知识
手写promise
- resolve
- reject
- then
不传参的解决 - 保证执行顺序,异步实现
then方法的代码在同步代码之后执行或者者状态改变之后执行,Promise参数函数中有异步操作时 - then的链式调用
- all
- catch
- race
前置知识
1. promise基础知识- promise是一个对象,可以通过new Promise()的方式生成。- promise是一个容器,存放着未来才会结束的事件,通常是一个异步操作的结果2. promise的特点- 对象的状态不受外界影响 ( pending, fulfilled, rejected ) - 只有异步操作的结果可以决定当前是那一种状态,其余操作都不能改变这个状态- 状态一旦改变,就不会再变,任何时候都能得到这个结果 - promise状态改变只有两种可能,从pending变为fulfilled,从pending变为rejected3. promise的缺点- 无法取消promise,一旦新建就会立即执行,无法中途取消- 假如不设置回调函数,promise内部抛出的错误不会反应到外部- 处于pending状态时,无法得知目前进展到那一个阶段(刚开始还是即将完成)4. resolve()- 将promise对象的状态由:pending变为fulfilled- 在异步操作(成功)时调用,并将异步操作的(结果)作为(参数)传递出去- 参数可以是(正常的值)或者者是一个(promise实例)- 当resolve()函数的参数是一个promise实例对象时:const p1 = new Promise( (resolve, reject) => { setTimeout(() => reject(new Error('fail')), 3000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(p1), 1000)})p2.then(result => console.log(result)).catch(error => console.log(error))解析:1. p2中resolve的是一个promise实例对象p1,导致p2的状态由p1决定2. 1s时 p2状态在改变,但是此时状态改变无效,应为resolve的是一个promise实例,p2状态由p1的状态决定3. 再过2s时,p1状态变为reject,会导致p2状态变为reject,被catch捕获5. reject()- 将promise对象的状态由:pending变为rejected- 在异步操作失败时调用,并将异步操作爆出的错误,通过参数传递出去- 参数通常是(Error对象的实例)6. 注意:resolve和reject函数并不会终止promise参数函数的执行,所以一般都会加上return防止后面的语句继续执行7. then() ------------------- 返回值是一个新的promie实例对象,后面可以接着调用then方法 ----------- promise实例对象生成后,可以then方法分别指定resolve状态和reject状态的回调函数- then方法有两个参数,第一个在状态变为fulfilled时调用,第二个在状态变为rejected时调用,第二个参数可选- then()方法的两个参数函数的 (参数 )是promise对象传出的值- then()定义在原型上,作用是增加状态改变时的回调函数- (then)方法返回的是:(新的promise实例对象),不是原来的promise实例- 注意: - 1. then方法的参数是状态改变后的回调函数,回调函数的参数是promise状态改变时抛出的值 - 2. 但是then方法有返回值,返回的是一个新的promise实例,可以链式调用then,此时后面的then的参数是前面then的返回值 - 3. 假如前面then返回的是promise对象,则后面的then的回调函数将在前面then状态改变后执行8. catch ------------------- 返回值是一个新的promie实例对象,后面可以接着调用then方法 ----------- 指定发生错误的回调函数- 是 .then(null, rejection)或者.then(undefined, rejection)的别名- 能捕获promise中抛出的错误,也能捕获then方法中抛出的错误- catch返回的是一个promise实例9. finally- 指定promise对象最后的状态无论为何,都会执行的操作- finally指定的回调函数,不接受任何参数,表明finally函数里面的操作与promise状态无关,不依赖promise执行的结果- 注意: - finally返回的是一个promise实例对象,并且是前面的值10. all- 用于将多个promise实例,包装成一个新的promise实例- 参数是一个数组(假如不是数组,必需是具备Iterator接口的数据类型),成员是promise实例- 假如参数数组的成员不是promise实例,会先调用Promise.resolve()将其转换成promise后在进一步解决- const p = Promise.all([p1, p2, p3]);(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。(2)只需p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。- 注意:1. 假如promise实例自己定义了catch方法,该promise实例中抛出的错误不会被promise.all的catch捕获2. 由于promise自己的catch方法返回的是一个新的promise实例,catch中没有抛错的话,到promise.all()时的参数数组的成员是这个新的promise实例3. 所以Promise.all()参数数组中的都是resolove状态4. 假如p2没有自己的catch方法,就会调用Promise.all()的catch方法const p1 = new Promise((resolve, reject) => { resolve('hello');}).then(result => result).catch(e => e);const p2 = new Promise((resolve, reject) => { throw new Error('报错了');}).then(result => result).catch(e => e);Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));// ["hello", Error: 报错了]11. race- 将多个promise实例包装成新的promise实例- const p = Promise.race([p1, p2, p3]);1. 只需p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。2. 假如参数数组成员不是一个promise,则会先调用Promise.all()转成promise实例12. allSettled ------------------- 返回值是一个新的promie实例对象- settled:结束- 将多个promise实例包装成新的promise实例- 参数是一个数组,成员是promise实例- 在所有promise实例状态都改变后(不论是fulfilled还是rejected),包装实例才会结束- 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。const promises = [ fetch('/api-1'), fetch('/api-2'), fetch('/api-3'),];await Promise.allSettled(promises);removeLoadingIndicator();上面代码对服务器发出三个请求,等到三个请求都结束,不论请求成功还是失败,加载的滚动图标就会消----------const resolved = Promise.resolve(42);const rejected = Promise.reject(-1);const allSettledPromise = Promise.allSettled([resolved, rejected]);allSettledPromise.then(function (results) { console.log(results);});// [// { status: 'fulfilled', value: 42 },// { status: 'rejected', reason: -1 }// ]解析:1. Promise.allSettled返回的是新的promise实例,状态只可是 fulfilled 2. 状态改变成fulfilled的监听函数(即then函数)的参数是一个数组,成员都是对象3. 成员对象中都具备status属性,值是 'fulfilled'或者者'rejected'4. 成员对象中,当是 fulfilled时,具备value属性5. 成员对象中,当是 rejected时,具备reason属性6. 使用场景: - 有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()方法就很有用。 - Promise.all()不能保证所有操作都结束后在执行某些操作,由于Promise.all()的rejected状态是谁先rejected,则状态就变为rejected13. any // ----- (和all相反) (和all相反) (和all相反)- Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。- 只需参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态- 所有参数实例都变成rejected状态,包装实例就会变成rejected状态。- Promise.any()抛出的错误,不是一个一般的错误,而是一个 AggregateError 实例。 它相当于一个数组,每个成员对应一个被rejected的操作所抛出的错误。14. resolve- 参数1. 参数是一个 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。2. 参数是一个thenable对象(指的是具备then方法的对象) - 会将这个对象转为 Promise 对象,而后就立即执行thenable对象的then方法3. 参数不是具备then方法的对象,或者根本就不是对象 - Promise.resolve方法返回一个新的 Promise 对象,状态为resolved4. 不带有任何参数 - Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。15. try- Promise.try- //const f = () => console.log('now');Promise.try(f);console.log('next');// now// next手写promise
class MyPromise { constructor(executor) { if(typeof executor !== 'function') { throw new TypeError(` Promise resolver ${executor} is not a function`) } this.init() // 这里用try...catch是由于 // 在promise的参数函数中的错误需要被then中的第二个回调函数所捕获,或者者被catch函数所捕获 // 假如在promise的参数参数中抛出错误,就用catch捕获,用this.reject函数去改变状态和存储拒因 try { executor(this.resolve, this.reject) } catch(err) { this.reject(err) } } init = () => { this.status = MyPromise.PENDING // 状态 this.value = null // 终值 this.reason = null // 拒因 this.onFulfilledCallbacks = [] // 成功回调数组 this.onRejectedCallbacks = [] // 失败回调数组 } resolve = (value) => { // 状态的改变,成功回调的执行 if(this.status === MyPromise.PENDING) { this.status = MyPromise.FULFILLED this.value = value this.onFulfilledCallbacks.forEach(fn => fn(this.value)) // 用于异步的情况 } } reject = (reason) => { // 状态的改变,失败回调的执行 if(this.status === MyPromise.PENDING) { this.status = MyPromise.REJECTED this.reason = reason this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } then = (onFulfilled, onRejected) => { // 两个参数都是可选的 // 假如不是函数则被忽略 if(typeof onFulfilled !== MyPromise.Function) { // 假如第一个参数不是函数,就把第一个参数设置成函数,该函数返回成功时的终值 // 并且该函数要在状态变为fulfilled时执行,参数就是终值 onFulfilled = value => value } if(typeof onRejected !== MyPromise.Function) { // 假如第二个参数不是函数,就把第二个参数设置成函数,该函数返回失败时候的拒因 // 并且该函数要在状态变为fulfilled时执行,参数就是终值 onRejected = reason => { throw reason } } // 注意: // then方法要实现链式调用,必需返回一个新的实例,假如是原来的promise实例 // then方法返回一个新的promise实例,即promise2 let promise2 = new Promise((resolve2, reject2) => { if(this.status === MyPromise.FULFILLED) { // 这里用setTimeout的起因是:执行顺序 // 假如不用定时器,then方法会立即执行,则不是微任务的体现 setTimeout(() => { try { const x = onFulfilled(this.value) MyPromise.resolvePromise(promise2, x, resolve2, reject2) } catch(err2) { reject2(err2) } }, 0) } if(this.status === MyPromise.REJECTED) { setTimeout(() => { try { const x = onRejected(this.reason) MyPromise.resolvePromise(promise2, x, resolve2, reject2) } catch(err2) { reject2(err2) } }, 0) } if(this.status === MyPromise.PENDING) { // 当promise的参数函数中有异步操作时,then方法会优先于resolve()或者者reject()先执行 // 这样就是导致执行then()方法时,状态是pending状态 // 这是需要用一个数组来存储将来才会执行的onFulfilled函数 // 这里push进onFulfilledCallbacks的函数,将在resolve()函数中去执行 this.onFulfilledCallbacks.push(value => { setTimeout(() => { // 这里之所以还要用setTimeout是由于在成功的回调函数中,resolve()后面还有同步代码的话,要保证把同步执行完,在去执行resolve函数 // 如下: // console.log('4') // resolve('5') // console.log('6') // 要保证 4 6 5这样的顺序 try { const x = onFulfilled(value) MyPromise.resolvePromise(promise2, x, resolve2, reject2) } catch(err2) { reject2(err2) } }) }) this.onRejectedCallbacks.push(reason => { setTimeout(() => { try { const x = onRejected(reason) MyPromise.resolvePromise(promise2, x, resolve2, reject2) } catch(err2) { reject2(err2) } }) }) } }) return promise2 }}MyPromise.PENDING = 'PENDING'MyPromise.FULFILLED = 'FULFILLED'MyPromise.REJECTED = 'REJECTED'MyPromise.Function = 'function'MyPromise.resolvePromise = function(promise2, x, resolve, reject) { // x 与 promise 相等 if (promise2 === x) { reject(new TypeError('Chaining cycle detected for promise')) } let called = false if (x instanceof MyPromise) { // 判断 x 为 Promise x.then( value => { MyPromise.resolvePromise(promise2, value, resolve, reject) }, reason => { reject(reason) } ) } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // x 为对象或者函数 try { const then = x.then if (typeof then === 'function') { then.call( x, value => { if (called) return called = true MyPromise.resolvePromise(promise2, value, resolve, reject) }, reason => { if (called) return called = true reject(reason) } ) } else { if (called) return called = true resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { resolve(x) }}Promise.defer = Promise.deferred = function() { let dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd}module.exports = MyPromisedream2023/blog/tree/master/promise
then方法没有传参时
- 需要重写 onFulfilled函数,返回当前的终值 this.value
- 需要重写 onRejected函数,返回当前的拒因 this.reason,并抛出
then = (onFulfilled, onRejected) => { if (typeof onFulfilled !== this.FUNCTION) { // 没传onFulfilled参数,就重写该函数 // 将调用参数原样返回 // 这里没有直接写 (typeof onFulfilled !== 'function') 防止魔法字符串 onFulfilled = value => value } if (typeof onRejected !== this.FUNCTION) { // 没传onRejected参数,就重写该函数 // 抛出reason onRejected = reason => { throw reason } } if (this.status === this.FULFILLED) { // 是fulfilled状态是,才执行onFulfilled函数,参数是当前的终值 // 即状态改变时为成功时,增加的回调函数 // 这里传参和没有传参都会执行,没传参是执行重写过后的onFulfilled onFulfilled(this.value) } if (this.status === this.REJECTED) { onRejected(this.reason) } }如何保证执行顺序1
console.log(1)new Promise((resolve, reject) => { console.log(2) resolve(1)}).then(() => console.log(4))console.log(3)问题:如何保证执行顺序是 1234处理:说明then方法中的代码要异步执行,用定时器模拟处理说明:假如不用定时器,执行顺序是1243 then = (onFulfilled, onRejected) => { if (typeof onFulfilled !== this.FUNCTION) { // 没传onFulfilled参数,就重写该函数 // 将调用参数原样返回 // 这里没有直接写 (typeof onFulfilled !== 'function') 防止魔法字符串 onFulfilled = value => value } if (typeof onRejected !== this.FUNCTION) { // 没传onRejected参数,就重写该函数 // 抛出reason onRejected = reason => { throw reason } } if (this.status === this.FULFILLED) { // 是fulfilled状态是,才执行onFulfilled函数,参数是当前的终值 // 即状态改变时为成功时,增加的回调函数 // 这里传参和没有传参都会执行,没传参是执行重写过后的onFulfilled // 用定时器处理代码的执行顺序 // 用定时器保证then方法中的参数函数是在同步代码之后执行 setTimeout(() => onFulfilled(this.value), 0) } if (this.status === this.REJECTED) { setTimeout(() => onRejected(this.reason), 0) } } 如何保证执行顺序2
console.log(1)new Promise((resolve, reject) => { console.log(2) setTimeout(() => resolve()) // 当这里有异步操作时,上面的代码打印只有 123,注意 4 并未打印 // 起因是then()方法在resolve()方法前执行了,由于resolve是异步的,导致 then() 中的状态还是 pending 状态 // 而在then方法中并为增加状态是pending状态时的相关操作}).then(() => console.log(4))console.log(3)问题:打印出了123,但是并未打印4分析: 1. 起因是then()方法在resolve()方法前执行了,由于resolve是异步的,导致 then() 中的状态还是 pending 状态 2. 而在then方法中并为增加状态是pending状态时的相关操作处理: 1. 在then()方法中增加pending状态下的相关判断 - 并向 onFulfilledCallbacks 数组中push一个方方法,该方中去调用 onFulfilled 方法,参数是当前的value - 并向 onRejectedCallbacks 数组中 push 一个方法,该方中去调用 onRejected 方法,参数是当前的reason 2. 在resolve()方法中去循环 onFulfilledCallbacks 数组,并执行里面的函数,实参是 this.value 2. 在reject()方法中去循环 onRejectedCallbacks 数组,并执行里面的函数,实参是 this.reasonthen = (onFulfilled, onRejected) => { ... if (this.status === this.PENDING) { // pending状态push函数到onFulfilledCallbacks数组 this.onFulfilledCallbacks.push(value => onFulfilled(value)) this.onRejectedCallbacks.push(reason => onRejected(reason)) } } resolve = (value) => { if (this.status === this.PENDING) { this.status = this.FULFILLED this.value = value this.onFulfilledCallbacks.forEach(fn => fn(this.value)) // 执行数组中的函数,并传入实参 } }reject = (reason) => { if (this.status === this.PENDING) { this.status = this.REJECTED this.reason = reason this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } }如何保证执行顺序3
console.log(1)new Promise((resolve, reject) => { console.log(2) setTimeout(() => { resolve() console.log(4) })}).then(() => console.log(5))console.log(3)问题:上面代码输出 12354 , 而真正的promise应该输出 12345分析:由于resolve()后面还有同步代码,要保证后面的同步代码先执行处理:在向 onFulfilledCallbacks数组中push方法时,要再用 setTimeout包装,让resolve()后面的代码先执行then = (onFulfilled, onRejected) => { ... if (this.status === this.PENDING) { this.onFulfilledCallbacks.push(value => { setTimeout(() => { // 再用setTimeout包装,保证resolve()后面的代码先于 then的回调函数 执行 onFulfilled(value) }, 0) }) this.onRejectedCallbacks.push(reason => { setTimeout(() => { onRejected(reason) }, 0) }) } }then的链式调用
- 返回一个新的promise
- 新的promise中参数函数的resolve的是onFufiled函数执行后返回的值
then = (onFulfilled, onRejected) => { if(typeof onFulfilled !== 'function') { onFulfilled = (value) => value } if(typeof onRejected !== 'function') { onRejected = (reason) => { throw reason } } const promise2 = new Promise((resolve, reject) => { if (this.status === Promise.FULFILLED) { setTimeout(() => { try { const x = onFulfilled(this.value) // 将onFulfilled函数的返回值作为resolve()的参数,传给新的 then() 方法 resolve(x) // promise2的resolve的时机 } catch(err) { reject(err) // promise2的reject的时机 } }) } if (this.status === Promise.REJECTED) { setTimeout(() => { try { const x = onRejected(this.reason) resolve(x) } catch (err) { reject(err) } }) } if (this.status === Promise.PENDING) { this.onFulfilledCallbacks.push((value) => { setTimeout(() => { try { const x = onFulfilled(value) resolve(x) } catch(err) { reject(err) } }) }) this.onRejectedCallbacks.push((reason) => { setTimeout(() => { try { const x = onRejected(reason) resolve(x) } catch(err) { reject(err) } }) }) } }) return promise2 }Promise.all()
—— 和 Promise.any()相反
- 注意是静态方法,用于将多个promise实例包装成一个 (
新的promise实例) - 参数:(
数组或者者具备Iterator接口的数据类型,并且返回的每个成员都是promise实例)
参数是一个数组,成员是promise实例
假如不是promise实例,会先调用Promise.resolve()转化成promise实例 - 状态
所有数组成员promise都变成 fulfilled时,才会变成 fulfilled
只有一个成员的promise状态变成rejected,就会变成rejected
说明:1. Promise.all()返回的是一个新的promise,就可以使用then获取resolve和reject的结果2. 参数是一个数组或者者具备Iterator接口的数据3. 假如参数数组成员不是promise,就会被Promise.resolve()转成promise对象4. resolve的时机是所有参数成员都变成fulfilled状态时5. reject的时机是只需有一个rejected状态时Promise.all = (promises) => { // 返回一个新的promise实例 return new Promise((resolve, reject) => { const arr = [] let count = 0 // 记录fulfilled状态的promise个数 const promiseArr = Array.from(promises) // 参数除了数组还可以是具备Iterator接口的数据类型 const len = promiseArr.length for (let i = 0; i < len; i++) { Promise.resolve(promiseArr[i]).then(value => { // 假如参数不是promise,会调用Promise.resolve()转成promise count ++ // 进入这里,表示成功的回调,即fulfilled状态 arr[i] = value // 将该成功的promise装进数组 if (count === len) { console.log(count, 'count') resolve(arr) // 假如count和数组总长度相等,说明都是fulfilled状态了 // 所有resolve的时机就是所有都变成fulfilled状态是resolve } }, reject) } })}const a = Promise.resolve(1)const b = Promise.resolve(2)const c = new Promise(resolve => { setTimeout(() => { resolve(33) })})Promise.all([a, b, c]).then(value => console.log(value, 'value'))https://segmentfault.com/a/1190000012820865
https://juejin.im/post/5d0da5c8e51d455ca0436271
Promise.race()
Promise.race = (promises) => { return new Promise((resolve, reject) => { const promiseArr = Array.from(promises) const len = promises.length for(let i = 0; i < len; i++) { Promise.resolve(promiseArr[i]).then(value => { resolve(value) // 直接resolve第一个then是成功时的回调函数接收到的终值 }) } }) }说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 【手写】promise
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 【手写】promise