最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 闲着无聊,写个promise

    正文概述 掘金(若沉)   2020-12-09   416

    一、前言

      不折腾不前端,在现公司待了3年了,每一段时间都会去不同的公司面试,目的呢一是为了看下市场的动态,对前端有什么要求。二是为了测量自己在前端市场上能值多少钱。三是为了巩固知识,提升面试技巧。
      当然,不能挑那些你想要进的公司去做实验,像福厂、猪场等,他们会有你面试记录,影响之后的面试,得不偿失。
      最近在面试过程中,被问到了一个老生常谈的题目,promise的实现原理。所以决定写一篇关于promise源码实现的文章。

    二、promsie使用方式

      正常在实现某个api源码的时候,首先是能熟练使用这个api,从api上考虑如何去实现这个源码,这是我一直以来的方式。

    new MyPromise((resolve, reject) => {
    	
    }).then((data) => {
    	// do something
    }).then((data) => {
    	// do something
    }).catch((err) => {
    	// do something
    })
    

    三、源码实现

      非常简单的一个例子,可以看出promise是一个构造函数,有几个方法,resolve, reject, then,catch,resolve和reject改变状态,根据状态执行then或者catch,所以可以马上就写出promise的接结构

    Class Promise {
       constructor (executor) {
          this.status = 'pedding' // pedding fulfilled reject, 初始状态pedding
          this.value = null // resolve结果
          this.reason = null // reject原因
          // 专门存放成功的回调函数
          this.onResolvedCallbacks = [];
          // 专门存放成功的回调函数
          this.onRejectedCallbacks = [];
          let resolve = (value) => {
    	if (this.status !== 'pedding') {
              return
            }
            this.status = 'fulfilled'
            this.value = value
            this.onResolvedCallbacks.forEach(fn => fn(this.value))
          }
          let reject = (reason) => {
    	if (this.status !== 'pedding') {
              return
            }
            this.status = 'reject'
            this.reason = reason
            this.onResolvedCallbacks.forEach(fn => fn(this.value))
          }
          try {
              executor(resolve, reject) // 同步执行
          } catch(err) {
              reject()
          }
      }
      then (onfulfilled, onrejected) {
      	// 使用setTimeout模拟异步
        if (this.status === 'pedding') {
        	this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                  onfulfilled(this.value)
                }, 0)
            })
        	this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                  onrejected(this.value)
                }, 0)
            })
        } else if (this.status === 'fulfilled') {
        	setTimeout(() => {
              onfulfilled(this.value)
            }, 0)
        } else {
        	setTimeout(() => {
              onrejected(this.value)
            }, 0)
        }
      }
      catch (errHander) {
        this.then(null, errHander)
      }
    }
    

      一个简易的promise就完成了,但是运行时发现并不能链式调用,原因就在于then的返回值不是一个promise,没有then方法,所以报错。所以处理方法就是返回一个新的promise,那么有一个问题,为什么是新的promise而不是return this?

    四、链式调用

      因为为了保证逻辑的简单性,promise的状态只能从pedding变为fulfilled或者reject,这也是promise名字的由来。如果使用return this,且then回调函数执行报错,状态还是fulfilled,promise是无法catch到错误。 那么我们改一下代码

    then (onfulfilled, onrejected) {
      let newPromise = new MyPromise((resolve, reject) => {
        if (this.status === 'pedding') {
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => {
              onfulfilled(this.value)
            }, 0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              onrejected(this.reason)
            }, 0)
          })
        } else if (this.status === 'fulfilled') {
          setTimeout(() => {
            let value = onfulfilled(this.value)
          	reject(value)
          }, 0)
        } else {
          setTimeout(() => {
            let reason = onrejected(this.reason)
          	reject(reason)
          }, 0)
        }
      })
      return newPromise
    }
    

      至此,一个简易的promise就实现了,包含了then,catch方法,面试够用。

    五、优化

      细心的同学可能已经发现,如果onfulfilled如果返回的是一个promise呢?我们不希望then拿到的结果是一个promise,那么就要等这个promise resolve时才能继续执行then方法。所以我们再改造一下

    // 添加一个函数
    function resolvePromise(promise, x, resolve, reject) {
      if ((typeof x === 'object' && x != null && x._proto_.contructor === MyPromse.contructor)) {
      // 此时就认为它是一个promise
        let then = x.then
        then.call(x, y => { // 从then中能拿到该x的结果,再resolve出去
          resolvePromsie(promise, y, resolve, reject) // 一直递归
        }, r => {
            reject(r);
        })
      } else if(typeof x === 'function') {
        let value = x()
        resolvePromise(promise, value, resolve, reject)
      } else {
        resolve(x)
      }
    }
    // 改造下then方法
    then (onfulfilled, onrejected) {
      let newPromise = new MyPromise((resolve, reject) => {
        if (this.status === 'pedding') {
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => {
              let x = onfulfilled(this.value)
              resolvePromsie(newPromse, x, resolve, reject)
            }, 0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              let x = onrejected(this.reason)
              resolvePromsie(newPromse, x, resolve, reject)
            }, 0)
          })
        } else if (this.status === 'fulfilled') {
          setTimeout(() => {
            let value = onfulfilled(this.value)
          	resolvePromsie(newPromse, value, resolve, reject)
          }, 0)
        } else {
          setTimeout(() => {
            let reason = onrejected(this.reason)
          	resolvePromsie(newPromse, reason, resolve, reject)
          }, 0)
        }
      })
      return newPromise
    }
    

    六、总结

      大多数时候,我们再日常工作中是不用去写promise源码,但是为了不被外界看成“熟练的api打工人”,还是撸一撸源码,其次也是为了promise的设计思路,提升编程能力。更重要的是基本每个面试官都会问,虽然不知道他们自己会不会,但是准备着总是没错的。


    起源地下载网 » 闲着无聊,写个promise

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元