资讯专栏INFORMATION COLUMN

我能不能在不看别人怎么实现promise的情况下,自己实现一个promise?

JouyPub / 654人阅读

摘要:上网查了一下好像是标准第二步开写构造函数什么都不想,先写一个构造函数,就叫把。对构造函数再次做修改。并且可以一个值。给下一个继续调用。应该是一个新的。最后的版本总结后来去看了看别人实现的方法。

我能不能在不看别人怎么实现promise的情况下,自己实现一个promise?
都8102年为什么还要写promise实现? 

​ 年前和年后面试了几家公司, 虽然都挂了… 但是都谈到了一个面试题,就是promise. 虽然使用promise很简单,而且我回答的都没问题.

​ 但是面试官都问到了一个题目. "如果让你实现一个promise.all方法, 怎么实现 ? " 临时想了一个每个promise.then里用计数器+1, 在判断计数器是否等于参数Array[promise]的 length 来判断promise是否都完成的实现思路, 也不知道算不算是对的.

​ 然后就回来想自己能不能在不看任何人的代码的情况下, 实现一个promise。

第一步: 先稍微分析一下原生的Promise Promise 使用方式

var a = new Promise(function( resolve, reject ){})

new一个Promise实例,传入一个函数,里面有两个参数。

resolve:成功时调用,并将成功后的数据传进then方法里。

reject:失败的时候调用,并将失败的数据传进catch方法里。

Promise的原型方法

很简单,只有我们常见的then catch还有finally方法。不过finally方法应该不属于ES6标准的,所以先忽略。(上网查了一下好像是ES2018标准)

第二步:开写 2.1:构造函数

什么都不想,先写一个构造函数,就叫 Future 把。

因为Promise有两种状态,所以我给他加一个 status

function Future(func){
  this.status = null;
}
2.2:增加resolve和reject

​ 接着需要执行传入的函数,并传给他一个resolvereject方法。经常用Promise的同学应该知道 Promise.resolvePromise.reject

​ 但是没在原型里找到这两个方法,所以我就直接在Future上加这两个方法。

// 只要执行了resolve或者reject肯定要改变status, 所以对实例的status做更新
Future.resolve = function (data) {
  this.status = "resolve"
  this.data = data
}

Future.reject = function (data) {
  this.status = "reject"
  this.data = data
}

这两个这里的data要在then里用,所以还是得缓存起来,然后将这两个方法传进func里,这里对构造函数再做改动

function Future(func){
  this.status = null;
  this.data = null;
  
  func(Future.resolve, Future.resolve)
}

​ 但是这里有一个问题,resolve执行的时候,this并不是指向当前的promise实例的,这时我就想到了bind方法。所以必须在初始化的时候把resolve和reject的作用域给绑定好。对构造函数再次做修改。( 还要加上setTimeout , 加入even loop,这个是后加的)

function Future(func){
  if(typeOf func !== "function") throw new Errow("Future 的参数必须为函数");
  
  var _this = this;
  this.status = null;
  this.data = null;
  setTimeout(function () {
    func(Future.resolve.bind(_this), Future.resolve.bind(_this))
  })
}
2.3:实现then和catch
回顾一下then的使用方式:传入一个函数,在promise执行resolve后,才会调用,并且函数的参数就是调用resolve的时候传入的值。并且可以return一个值。给下一个then继续调用。

​ 所以then函数应该很简单,直接缓存这个函数,resolve的时候再拿出来调用即可。而关于链式调用,一开始想到的就是return this

​ 所以一开始我先是这么写的

function Future(func){
      //再加一个函数队列数组和一个错误状态的执行函数
      this.queue = [];
      this.failCb = null;
     ...其余代码省略
}
Future.prototype.then = function (callback) {
      if(typeof callback !== "function") throw new Errow("then必须传入函数");
  
    if(this.status === "resolve"){
        this.data = callback(this.data);
    }else if(this.status === null){
        this.queue.push(callback);
   }

   return this;
}

Future.prototype.catch = function (callback) {
      if(typeof callback !== "function") throw new Errow("catch必须传入函数");
  
    if (this.status === "reject") {
        this.data = callback(this.data);
    }else if(this.status === null){
        this.failCb = callback;
    }
  
    return this;
}
2.4:实现resolve和reject

​ 其他的都好了,接着就是在resolve里去执行队列里的函数。reject里执行错误函数。

Future.resolve = function (data) {
    var context = this;
    context.status = "resolve";
    context.data = data;
  
      //先把第一个函数拿出来
    var func = context.queue.shift();
    if(func){
        try{
            var d = func(data);
              //函数可以返回一个值,也可以返回一个promise
            if(d instanceof Future){
                d = d.data;
            }
              //递归的方式再执行下一个,这里再用call去改变this的指向
            Future.resolve.call(context, d);
        }catch(err){
              //捕捉报错,执行catch
            Future.reject.call(context, err);
        }
    }
}

Future.reject = function (data) {
    this.status = "reject";
    this.data = data;
    if(this.failCb){
        this.failCb(data)
    }else{
        throw new Error("promise catch")
    }
}

以上。

​ 到这里呢,就是那时临时想临时做的第一版。

后记

​ 当然,后面又大改了一些东西。最主要的是then函数不应该返回this。应该是一个新的promise。如果按照现在这么做,经过多个then之后,初始的data就变成了最后一个值了。我们希望的是要保留最初初始化的时候的那个值。

//比如
var a = new Future(function(resolve, reject){
    setTimeout(function(){
        console.log("success")
        resolve(true)
    }, 1000)
})

a.then(function(res){
    console.log(res);
    return "啦拉拉"
})

setTimeout(function(){
    a.then(function(res){
          //这里就会输出 "啦拉拉"。其实期望的是输出 true
        console.log("settimeout: ", res)
    })
},2000)

​ 后来为了解决这个,突然陷入了牛角尖。。。花了一天才做完。水平有限,只能做到这样了,最后附上完整代码吧。

最后的版本

总结

​ 后来去看了看别人实现的方法。大体思路应该也是差不多的。其实就做个记录总结,方便以后面试用。嘻嘻(^__^)。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94033.html

相关文章

  • 让你从头到尾把promise明明白白

    摘要:它的作用是为实例添加状态改变时的回调函数。里面有两个回调函数,前者返回的回调函数,后者是可选值。可以看成是的别名专门用来指定错误发生时的回调函数。 最近一直私下在看Android项目,前端这一块没怎么仔细研究。昨天在写重构公司前端项目的时候,我发现一旦有异步的任务,脑海里面条件反射一般的出现promise的字样。重构的多了 心就就在纳闷:既然promise这么好用,我能不能自己手写一个pro...

    silenceboy 评论0 收藏0
  • babel入门

    摘要:为了方便,团队将一些集合在一起,并称之为。先看一下直观体验源代码配置编译后的代码通过对比可以看出,第二种方案直接从引入,避免自己定义,从而减少代码的体积。 Babel Babel 是一个 JavaScript 编译器,它可以将ES6+语法编译为浏览器支持的ES5语法。要学好babel必须先理解相关的概念,但是你刚起步就去扣这些细节的话,很可能因为babel一些复杂而模糊的概念打击你的信...

    pkwenda 评论0 收藏0
  • ES6常用知识学习札记

    摘要:在年正式发布了,简称,又称为。再次简写循环迭代数组每个元素都执行一次回调函数。方法用于调用数组的每个元素,并将元素传递给回调函数。注意对于空数组是不会执行回调函数的。 转载请注明出处 原文连接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9d es6 是什么 首先弄明白ECMA和js的关系。ECMA是标准,Jav...

    googollee 评论0 收藏0
  • ES6常用知识学习札记

    摘要:在年正式发布了,简称,又称为。再次简写循环迭代数组每个元素都执行一次回调函数。方法用于调用数组的每个元素,并将元素传递给回调函数。注意对于空数组是不会执行回调函数的。 转载请注明出处 原文连接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9d es6 是什么 首先弄明白ECMA和js的关系。ECMA是标准,Jav...

    tracymac7 评论0 收藏0

发表评论

0条评论

JouyPub

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<