【手写】promise

作者 : 开心源码 本文共13575个字,预计阅读时间需要34分钟 发布时间: 2022-05-13 共217人阅读

大纲
前置知识
手写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 = MyPromise

dream2023/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

发表回复