都8102年为何还要写promise实现? 年前和年后面试了几家公司, 虽然都挂了… 可是都谈到了一个面试题,就是promise. 虽然使用promise很简单,并且我回答的都没问题.javascript
可是面试官都问到了一个题目. "若是让你实现一个promise.all方法, 怎么实现 ? " 临时想了一个每一个promise.then里用计数器+1, 在判断计数器是否等于参数Array[promise]的 length 来判断promise是否都完成的实现思路, 也不知道算不算是对的.java
而后就回来想本身能不能在不看任何人的代码的状况下, 实现一个promise。git
var a = new Promise(function( resolve, reject ){})github
new一个Promise实例,传入一个函数,里面有两个参数。面试
resolve
:成功时调用,并将成功后的数据传进then
方法里。数组
reject
:失败的时候调用,并将失败的数据传进catch
方法里。promise
很简单,只有咱们常见的then
catch
还有finally
方法。不过finally
方法应该不属于ES6标准的,因此先忽略。(上网查了一下好像是ES2018标准)缓存
什么都不想,先写一个构造函数,就叫 Future 把。函数
由于Promise有两种状态,因此我给他加一个 status
。oop
function Future(func){ this.status = null; }
接着须要执行传入的函数,并传给他一个resolve
和reject
方法。常常用Promise的同窗应该知道 Promise.resolve
和 Promise.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)) }) }
回顾一下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; }
其余的都好了,接着就是在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)
后来为了解决这个,忽然陷入了牛角尖。。。花了一天才作完。水平有限,只能作到这样了,最后附上完整代码吧。
后来去看了看别人实现的方法。大致思路应该也是差很少的。其实就作个记录总结,方便之后面试用。嘻嘻(^__^)。