美文网首页JavaScript
[深入19] 手写Promise

[深入19] 手写Promise

作者: woow_wu7 | 来源:发表于2021-09-22 08:40 被阅读0次
    image

    导航

    [深入01] 执行上下文
    [深入02] 原型链
    [深入03] 继承
    [深入04] 事件循环
    [深入05] 柯里化 偏函数 函数记忆
    [深入06] 隐式转换 和 运算符
    [深入07] 浏览器缓存机制(http缓存机制)
    [深入08] 前端安全
    [深入09] 深浅拷贝
    [深入10] Debounce Throttle
    [深入11] 前端路由
    [深入12] 前端模块化
    [深入13] 观察者模式 发布订阅模式 双向数据绑定
    [深入14] canvas
    [深入15] webSocket
    [深入16] webpack
    [深入17] http 和 https
    [深入18] CSS-interview
    [深入19] 手写Promise
    [深入20] 手写函数

    [react] Hooks

    [部署01] Nginx
    [部署02] Docker 部署vue项目
    [部署03] gitlab-CI

    [源码-webpack01-前置知识] AST抽象语法树
    [源码-webpack02-前置知识] Tapable
    [源码-webpack03] 手写webpack - compiler简单编译流程
    [源码] Redux React-Redux01
    [源码] axios
    [源码] vuex
    [源码-vue01] data响应式 和 初始化渲染
    [源码-vue02] computed 响应式 - 初始化,访问,更新过程

    前置知识

    一些单词

    race:比赛,竞赛
    Settled:结束
    
    execute:执行
    executor:执行者
    
    detected:检测
    

    promise复习

    • 方法
      • promise.then() ---------------------------- 返回新的promise
      • promise.catch() --------------------------- 返回新的promise
      • promise.finally() -------------------------- 不管状态如何都会执行
      • promise.all() ------------------------------ 所有resolve则fulfilled,一个reject则rejected
      • promise.any() ---------------------------- 一个resolve则fulfilled,所有reject则rejected
      • promsie.race() ---------------------------- 第一个resolve则fulfiled,第一个reject则rejected
    • 特点
      • 对象的状态不受外界影响,只有异步操作的结果才可以决定当前是哪一种状态,任何其他操作都不能改变这个状态
      • 状态一旦改变就不会再变,任何时候都可以得到这个结果
    • <font color=red>缺点</font>
      • 无法取消promise,一旦新建就会立即执行,中途无法取消
      • 如果不设置回调,内部抛出的错误,不会反应到外部
      • 当处于pending状态时,无法得知当前进展到哪一个阶段 ( 刚开始?即将结束?)
    • Promise的用法
    const promise = new Promise(function(resolve, reject) {
      // ... some code
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    
    
    说明:
    (1) Promise构造函数接受一个 ( 函数 ) 作为参数
    (2) ( 参数函数 ) 又接受两个函数作为参数 ( resolve函数 ) 和 ( reject函数 )
    (3) resolve() 
        - 函数是在状态由 pending->fulfilled 时,即成功时调用,并将异步操作的结果作为参数传递出去
        - resolve()函数的参数,除了是正常的 ( 值 ) 以外,还可以是一个 ( promise实例 )
    (4) reject() 
        - 函数是在状态由 pending->rejected时,即失败时调用,并将异步操作报出的错误作为参数传递出去
        - reject()函数的参数,通常是 ( Error对象 ) 的实例
    (5) then() 方法接受两个函数作为参数
        - 第一个参数函数在resolve时被调用
        - 第二个参数函数在reject时被调用,参数分别是终值和拒因,第二个参数函数可选
    
    • resolve()
      • resolve()和reject()两个函数并不会终止promise,后面的代码还是会执行
      • <font color=red>通常用 return resolve() return reject() 这样的方式结束promise构造函数代码的运行</font>
      • <font color=blue>resolve() 函数的参数是一个promise实例的情况</font>
    let count = 1
    setInterval(() => {console.log(count++)}, 1000)
    
    const p1 = new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('p1中的代码开始执行')
        reject(new Error('fail'))
      }, 3000)
    })
    
    const p2 = new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('p2中的代码开始执行');
        resolve(p1)
      }, 1000)
    })
    
    p2
      .then(result => console.log(result))
      .catch(error => console.log(error))
      
    
    分析:
    (1) p2的状态在 1s 后改变为成功,resolve(p1)的参数p1还是一个promise,导致p2的状态失效
    (2) p2的状态失效,则p2的状态由p1决定
    (3) p1的状体在 3s 后改变为失败, reject(new Error('fail')),所以在 3s 后,p2的状态也变成了失败
    (4) p2的失败状态由 p2.catch()捕获
    
    • then()
      • <font color=red>返回一个新的promise实例,因此可以采用链式写法</font>
      • <font color=blue>如果then()方法链式调用,上一个then()可能返回的是一个promise实例,则后一个then()的回调函数需要等待前一个then()状态改变后才会调用</font>
    • catch()
      • <font color=red>返回一个新的promise实例,主要作用是捕获promise过程中的错误</font>
      • <font color=blue>catch()方法返回的是一个promise对象,因此.catch()后面可以继续调用.then()</font>
      • catch()是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数
      • 只要是catch()前面的then()和promise内部抛出的错误都能被catch()捕获
      • 注意:
        • 一个在then()中只需要添加成功状态后的回调,而失败的状态由catch()来负责捕获
    • finally()
      • <font color=red>不管promise对象最后是什么状态,都会执行的操作</font>
      • <font color=blue>finally()的回调函数不接受任何参数,因为finally()的回调函数中的操作和状态无关</font>
    • all()
      • 所有resolve()则fulfilled,一个reject()则rejected
    • any()
      • 一个resolve()则fulfilled,所有reject()则rejected
      • 和all()相反
    • race()
      • 一个resolve()则fulfilled,一个reject()则rejected
    • allSettled()
      • 在所有参数的实例都返回结果时,包装实例才会结束
      • 比如等待所有请求完成,无论成功失败,都结束loading动画

    手写Promise

    class Promise {
      constructor(executor) {
        // 参数不是函数,报错
        if (typeof executor !== 'function') {
          throw new TypeError(`Promise resolver ${executor} is not a function`)
        }
        this.init() // 初始化值
        try {
          executor(this.resolve, this.reject)
        } catch (err) {
          // 使用 try...catch 的目的是为了把executor()中的错误抛出给then的回调去捕获
          this.reject(err)
        }
      }
    
    
      init = () => {
        this.value = null // 终值
        this.reason = null // 拒因
        this.state = Promise.PENDING // 状态
    
        this.onFulfilledCallbacks = [] // 成功回调, 在then()方法中push,resolve()时执行
        this.onRejectedCallbacks = [] // 失败回调,在then()方法中push,reject()时执行
      }
    
      resolve = (value) => {
        // 成功后的一系列操作 (状态的改变,成功回调的执行 )
        // 状态的改变:pending -> fulfilled
    
        // console.log(this.constructor === Promise) // true
        // this 在箭头函数中,作用域绑定在父级执行上下文,即定义时所在的对象
        // 即this相当于父级的this,这里又是在勾走函数中,所以this指向了实例对象 
        if (this.state === Promise.PENDING) {
          this.state = Promise.FULFILLED
          this.value = value
    
          this.onFulfilledCallbacks.forEach(fn => fn(this.value))
          // 当promise的参数函数中有异步操作时,then方法会优先于resolve()或者reject()先执行
          // 这样就是导致执行then()方法时,状态是pending状态,因为状态的改变是在resolve()或reject()中改变的,而他们因为异步都没执行
          // 这是需要用一个数组来存储将来才会执行的onFulfilled函数
          // 这里push进onFulfilledCallbacks的函数,将在resolve()函数中去执行
        }
      }
    
      reject = (reason) => {
        // 失败后的一系列操作 (状态的改变,失败回调的执行 )
        // 状态的改变:pending -> rejected
        if (this.state === Promise.PENDING) {
          this.state = Promise.REJECTED
          this.reason = reason
    
          this.onRejectedCallbacks.forEach(fn => fn(this.reason))
        }
      }
    
      then = (onFulfilled, onRejected) => {
        // 参数校验,穿透效果,即then不传任何参数具有穿透效果
        if (typeof onFulfilled !== 'function') {
          onFulfilled = value => value
        }
        // 参数校验,穿透效果,即then不传任何参数具有穿透效果
        if (typeof onRejected !== 'function') {
          onRejected = reason => {
            throw reason
          }
        }
    
        // then()方法返回的是一个新的 promse 实例
        // 因为返回新的promise实例,可以可以实现链式调用
        let promise2 = new Promise((resolve2, reject2) => {
          // 执行onFulfilled函数的条件
          if (this.state === Promise.FULFILLED) {
            setTimeout(() => {
              // 这里中间的then()方法中的回调 onFulfilled() 函数是有返回值的
              // 中间then()参数函数onFulfilled()的返回值,会被当做下一个then回调的参数传入
              try {
                const x = onFulfilled(this.value)
                Promise.resolvePromise(promise2, x, resolve2, reject2)
              } catch (err) {
                reject2(err)
              }
    
            })
          }
          if (this.state === Promise.REJECTED) {
            setTimeout(() => {
              try {
                const x = onRejected(this.reason)
                Promise.resolvePromise(promise2, x, resolve2, reject2)
              } catch (err) {
                reject2(err)
              }
    
            })
          }
          if (this.state === Promise.PENDING) {
            // 如果状态是 pending
            // 当promise的参数函数中有异步操作时,then方法会优先于resolve()或者reject()先执行
            // 这样就是导致执行then()方法时,状态是pending状态,因为状态的改变是在resolve()或reject()中改变的,而他们因为异步都没执行
            // 这时需要用一个数组来存储将来才会执行的onFulfilled函数
            // 这里push进onFulfilledCallbacks的函数,将在resolve()函数中去执行
            this.onFulfilledCallbacks.push((value) => {
              // 这里仍然需要使用setTimeout,因为这个函数是在resolve()中执行的,如果resolve()后面任然后同步代码,要保证同步代码先执行
              setTimeout(() => {
                try {
                  const x = onFulfilled(value)
                  Promise.resolvePromise(promise2, x, resolve2, reject2)
                } catch (err) {
                  reject2(err)
                }
              })
            })
            this.onRejectedCallbacks.push((reason) => {
              setTimeout(() => {
                try {
                  const x = onRejected(reason)
                  Promise.resolvePromise(promise2, x, resolve2, reject2)
                } catch (err) {
                  reject2(err)
                }
    
              })
            })
          }
        })
        return promise2
      }
    }
    
    // 这里使用静态属性,是为了避免 魔法字符串
    Promise.PENDING = 'pending'
    Promise.FULFILLED = 'fulfilled'
    Promise.REJECTED = 'rejected'
    Promise.resolvePromise = function (promise2, x, resolve, reject) {
      // x 与 promise2 相等
      if (promise2 === x) {
        reject(new TypeError('chainning cycle detected for promise'))
      }
      // x 是 Promise
      if ( x instanceof Promise) {
        x.then(value => {
          // resolve(value)
          Promise.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)
      }
    }
    
    
    const promise = new Promise((resolve, reject) => {
      // throw new Error('出错了')
      console.log(1)
      setTimeout(() => {
        console.log(4)
        resolve(6)
        console.log(5)
      })
      console.log(2)
    })
      .then(
        value => {
          console.log(value, 'value')
          return new Promise(resolve => {
            resolve(new Promise(resolve3 => {
              resolve3(7)
            }))
          })
        },
        reason => {
          console.log(reason, 'reason')
        })
      .then(
        value => {
          console.log(value, 'vvvvvvvvvvvv')
        }, reason => {
          console.log(reason)
        })
    console.log(3)
    
    
    
    // then
      // then中的两个参数回调函数需要异步,setTimeout解决
      // 如果promise参数函数内部抛出错误,需要在then()中捕获 => try ... catch
      // 如果promise中存在异步,then的回调不会执行 => 因为在执行then方法的时,state === 'pending' 不满足执行then两个回调的任何一个,而当setTimeout中的 resolve() 执行的时,then执行过了就不会再继续执行
    

    new Promise()的参数必须是函数,非函数时会报错

    原生:
    new Promise(1)
    TypeError: Promise resolver 1 is not a function
    
    
    模拟实现:
    class Promise {
      constructor(executor) {
        if (typeof executor !== 'function') {
          // 参数必须是函数,不是函数抛出错误
          throw new TypeError(`Promise resolver ${executor} is not a function`)
        }
        executor(this.resolve, this.reject)
      }
    }
    const promise = new Promise()
    // TypeError: Promise resolver undefined is not a function
    

    resolve()方法的主要作用

    • (1) 把状态 ( <font color=red>status</font> ) 从 ( <font color=red>pending -> fulfilled</font> )
    • (2) 把终值 ( <font color=red>value</font> ) 赋值为 resolve()函数传入的 ( <font color=red>参数</font> )
    • (3) 把 ( <font color=red>onFulfilledCallback数组</font> ) 中的函数,依此取出执行
      • 如果promise中存在异步操作时,then()比resolve()先执行
      • 所以在then()方法中,需要向onFulfilledCallback数组中push进一个将来在resolve()中才会执行的函数

    rejected()方法的主要作用

    • (1) 把状态 ( <font color=red>status</font> ) 从 ( <font color=red>pending -> rejected</font> )
    • (2) 把拒因 ( <font color=red>value</font> ) 赋值为 reject()函数传入的 ( <font color=red>参数</font> )
    • (3) 把 ( <font color=red>onRejectedCallback数组</font> ) 中的函数,依此取出执行
      • 如果promise中存在异步操作时,then()比resolve()先执行
      • 所以在then()方法中,需要向onFulfilledCallback数组中push进一个将来在reject()中才会执行的函数
    class Promise {
      constructor(executor) {
        if (typeof executor !== 'function') {
          // 参数必须是函数,不是函数抛出错误
          throw new TypeError(`Promise resolver ${executor} is not a function`)
        }
        this.init()
        executor(this.resolve, this.reject)
      }
      init = () => {
        this.value = null // 终值,初始化
        this.reason = null // 拒因,初始化
        this.status = Promise.PENDING // 状态,初始化时pending
        this.onFulfilledCallbacks = [] // 成功回调, 在then()方法中push,resolve()时执行
        this.onRejectedCallbacks = [] // 失败回调,在then()方法中push,reject()时执行
      }
      resolve = (value) => {
        if (this.status === Promise.PENDING) {
          this.status = Promise.FULFILLED
          this.value = value
          this.onFulfilledCallbacks.forEach(fn => fn(value))
        }
      }
      reject = (reason) => {
        if (this.status === Promise.PENDING) {
          this.status === Promise.REJECTED
          this.reason = reason
          this.onRejectedCallbacks.forEach(fn => fn(resaon))
        }
      }
      then = (onFulfilled, onRejected) => {
        if (this.status === Promise.FULFILLED) {
          onFulfilled(this.value)
        }
        if (this.status === Promise.REJECTED) {
          onRejected(this.reason)
        }
        if (this.status === Promise.PENDING) {
          this.onFulfilledCallbacks.push((value) => onFulfilled(value))
          this.onRejectedCallbacks.push((reason) => onRejected(reason))
        }
      }
    }
    
    Promise.PENDING = 'pending'
    Promise.FULFILLED = 'fulfilled'
    Promise.REJECTED = 'rejected'
    
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(1)
      })
    }).then(value => {
      console.log(value)
    }, reason => {
      console.log(reason)
    })
    

    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)
            }
        }
    
    

    then()保证执行顺序1

    • <font color=red>then()需要在同步代码执行完后,then()中的回调函数才能执行</font>
    console.log(1)
    const promise = new Promise((resolve, reject) => {
      console.log(2)
      resolve(5)
      console.log(3)
    })
    .then(value => {
      console.log(value)
    }, reason => {
      console.log(reason)
    })
    console.log(4)
    
    
    问题:如何保证执行顺序是  12345
    解决:then()是异步方法,即then()方法的参数回调需要在resolve()或reject()方法执行后才执行,用 ( 定时器 ) 解决
    说明:如果不用定时器执行顺序是 
    
    
    then = (onFulfilled, onRejected) => {
        if (typeof onFulfilled !== Promise.FUNCTION) {
          onFulfilled = value => value
        }
        if (typeof onRejected !== Promise.FUNCTION) {
          onRejected = reason => reason
        }
        if (this.status === Promise.FULFILLED) {
          setTimeout(() => { // 用setTimeout()来模拟异步执行onFulfilled,保证同步代码执行后再执行onFulfilled
            onFulfilled(this.value)
          })
        }
        if (this.status === Promise.REJECTED) {
          setTimeout(() => { // 用setTimeout()来模拟异步执行onRejected,保证同步代码执行后再执行onRejected
            onRejected(this.reason)
          })
        }
        if (this.status === Promise.PENDING) {
          this.onFulfilledCallbacks.push((value) => onFulfilled(value))
          this.onRejectedCallbacks.push((reason) => onRejected(reason))
        }
      }
    

    then()保证执行顺序2

    • <font color=red>当promise中有有异步代码时,then()方法会比resolve()先执行,此时statuss='pending'状态</font>
    • <font color=red>而在then方法中并未添加状态是pending状态时的相关操作时,then()中的两个回调都不会执行</font>
    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.reason
    
    
    
    then = (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))
            }
        }
    

    then()保证执行顺序3

    console.log(1)
    new Promise((resolve, reject) => {
        console.log(2)
        setTimeout(() => {
            resolve()
            console.log(4) // 要保证4比5先执行,因为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() 的链式调用

    • <font color=red>then()方法返回的是新的promise实例,注意不是原来的,所以可以链式调用</font>
    • <font color=red>新的promise中参数函数的resolve的是onFufiled函数执行后返回的值</font>
    then = (onFulfilled, onRejected) => {
        // 参数校验,穿透效果,即then不传任何参数具有穿透效果
        if (typeof onFulfilled !== Promise.FUNCTION) {
          onFulfilled = value => value
        }
        // 参数校验,穿透效果,即then不传任何参数具有穿透效果
        if (typeof onRejected !== Promise.FUNCTION) {
          onRejected = reason => reason
        }
        const promise2 = new Promise((resolve2, reject2) => {
          if (this.status === Promise.FULFILLED) {
            setTimeout(() => {
              const x = onFulfilled(this.value)
              // 将onFulfilled函数的返回值作为resolve()的参数,传给新的 then() 方法
              resolve2(x)
            })
          }
          if (this.status === Promise.REJECTED) {
            setTimeout(() => {
              const x = onRejected(this.reason)
              reject2(x)
            })
          }
          if (this.status === Promise.PENDING) {
            this.onFulfilledCallbacks.push((value) => {
              setTimeout(() => {
                const x = onFulfilled(value)
                resolve2(x)
              })
            })
            this.onRejectedCallbacks.push((reason) => {
              setTimeout(() => {
                const x = onRejected(reason)
                reject2(x)
              })
            })
          }
        })
        return promise2
      }
    
    更详细
    
    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()模拟实现

    • p = Promsie.all([p1, p2, p3])
    • 返回值:Promise.all() 返回一个新的promise实例
    • 参数:
      • ( <font color=red>参数</font> ) 是一个 ( <font color=red>数组</font> ) 或者是具有 ( <font color=red>Iterator</font> ) 接口类型的数据,成员都是promsie实例
      • 如果不是promsie实例就会调用Promsie.resolve()转成promsie实例
    • 作用:
      • 如果所有参数数组成员的状态,都变成了fulfilled,则整个状态变成fulfilled
        • 此时 p1,p2,p3中resolve()的返回值,将组成一个数组,传递给p的回调函数
      • 如果有一个参数数组成员的状态变成了rejected,则整个状态变成了rejected
        • 此时,第一个被拒绝的reject()的返回值,会传递给p的回调函数
    说明:
    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'))
    

    2021/4/7修改 Promise.all 模拟实现

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body>
      <script>
        // 手写 Promise.all
        // 1. Promise.all返回的是一个新的promise
        // 2. Promise.all的参数是一个数组,成员是promise对象,如果不是promise对象会先把参数转成promise
        // 3. 所有成员fulfilled则整个状态变成 fulfilled
        // 4. 一个rejected则整个状态变成 rejected
    
        const promise1 = Promise.resolve(1)
        const promise2 = Promise.resolve(2)
        const promise3 = Promise.resolve(3)
        const promise4 = Promise.reject(new Error('出错了'))
    
        Promise.all2 = (promises) => {
          return new Promise((resolve, reject) => { // Promise.all()返回一个新的promise对象
            const promisesArr = Array.from(promises)
            const len = promisesArr.length
    
            let count = 0
            const results = []
    
            for (let i = 0; i < len; i++) {
              Promise.resolve(promisesArr[i]).then(value => { // 如果参数不是promise则先转成promise
                count++
                results.push(value)
                if (results.length === len) {
                  resolve(results) // 当数组整个遍历完后,都没发生错误的情况下,resolve(results) 整个终值数组
                }
              }, reason => reject(reason)) // 只要一个产生错误,整个 reject最先发生的拒因
            }
          })
        }
    
        Promise.all2([promise1, promise2, promise3]) // fulfilled
          .then(value => console.log(value))
          .catch(err => console.log(err))
    
        Promise.all2([promise1, promise2, promise3, promise4]) // rejected
          .then(value => console.log(value))
          .catch(err => console.log(err))
      </script>
    </body>
    </html>
    

    Proimse.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是成功时的回调函数接收到的终值
              })
            }
          })
        }
    

    总结遇到的一些面试题 2021/04/10

    (1) promise面试题1

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <script>
        console.log(1)
        setTimeout(() => { // 定时器 A
          console.log(2);
          Promise.resolve().then(() => { // Promise D
            console.log(3)
          })
        })
        new Promise((resolve, reject) => { // Promise B
          console.log(4)
          resolve(5)
        }).then((data) => {
          console.log(data)
        })
        setTimeout(() => { // 定时器 C
          console.log(6)
        })
        console.log(7)
    /****
             答案:1 4 7 5 2 3 6
        
             分析:
             1. 第一次事件循环
             同步任务:1
             宏任务:[A, C]
             微任务:[B]
             输出:1 4 7
        
             2. 第二次事件循环
             遍历所有微任务[B] => 5
             取出第一个宏任务[A] => 2 , 并将微任务D添加进任务队列,此时微任务队列 [D]
             输出:5 2
        
             3. 第三次事件循环
             遍历所有微任务[D] => 3
             取出第一个红任务[C] -> 6
             输出:3 6
    */
      </script>
    </body>
    </html>
    

    (2) promise面试题2

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <script>
        console.log(1)
        // A promise
        new Promise((resolve) => {
          console.log(2)
          resolve()
          console.log(3)
        }).then(res => { // E then
          console.log(4)
          // C 定时器
          setTimeout(() => console.log(5))
        })
        console.log(6)
        // B 定时器
        setTimeout(() => {
          console.log(7)
          // D promise
          new Promise((resolve) => {
            console.log(8)
            resolve()
          }).then(() => console.log(9)) // F then
        })
        console.log(10)
        /**
         * 第一轮 Event loop
         * 1 => 同步任务,进入函数调用栈,立即执行
         * A => A的回调立即执行
         *      2 => 同步任务,立即执行
         *      E => 微任务,进入微任务队列
         *      3 => 同步任务,立即执行
         * 6 => 同步任务,立即执行
         * B => 宏任务,B的回调进入宏任务队列
         * 10 => 同步任务,立即执行
         * 此时执行情况如下:
         * 输出:1,2,3,6,10
         * 微任务:[E]
         * 宏任务:[B]
         *
         * 第二轮 Event loop
         * 清空微任务队列,取出宏任务队列的第一个成员
         * E => 4 同步任务,立即执行
         *      C 宏任务,进入宏任务队列,此时的宏任务队列 [B, C]
         * B => 7 同步任务,立即执行
         *      D promise的回调立即执行
         *        => 8 同步任务,立即执行
         *        => F 微任务,进入微任务队列,此时的微任务队列 [F]
         * 此时执行情况如下:
         * 输出:4,7,8
         * 微任务:[F]
         * 宏任务:[C]
         *
         * 第三轮 Event loop
         * 清空微任务队列,取出宏任务队列的第一个成员
         * F => 9 同步任务,立即执行
         * C => 5 同步任务,立即执行
         *
         * 总的输出顺序:1,2,3,6,10,4,7,8,9,5
         */
      </script>
    </body>
    </html>
    

    资料

    手写promise https://segmentfault.com/a/1190000012820865
    手写promise https://juejin.im/post/6844903872842956814

    相关文章

      网友评论

        本文标题:[深入19] 手写Promise

        本文链接:https://www.haomeiwen.com/subject/zbewgltx.html