转自: http://www.jianshu.com/p/473cd754311fjavascript
看了些promise的介绍,仍是感受不够深刻,这个在解决异步问题上是一个很好的解决方案,因此详细看一下,顺便按照本身的思路实现一个简单的Promise。html
promise
是一个拥有符合上面的特征的then方法的对象或者方法。thenable
是定义了then方法的对象或者方法value
是任何合法的js的值(包括undefined,thenable或者promise)exception
是一个被throw申明抛出的值reason
是一个指明了为何promise被拒绝then方法必须也返回一个promise(这个promise能够是原来的promise,实现必须申明什么状况下二者能够相等)promise2 = promise1.then(onFulfilled, onRejected);java
onFulfilled
和onRejected
都返回一个value x,执行2.3Promise的解决步骤[Resolve]onFulfilled
和onRejected
都抛出exception e,promise2必须被rejected一样的eonFulfilled
不是个function,且promise1 is fulfilled,promise2也会fulfilled,和promise1的值同样onRejected
不是个function,且promise1 is rejected,promise2也会rejected,理由和promise1同样这里不论promise1被完成仍是被拒绝,promise2 都会被 resolve的,只有出现了一些异常才会被rejectedgit
promise
和一个值x
做为输入的一个抽象操做。若是这个x是支持then的,他会尝试让promise接受x的状态;不然,他会用x的值来fullfill这个promise。运行这样一个东西,遵循如下的步骤
若是promise产生了环形的嵌套,好比[Resolve]最终唤起了[Resolve],那么实现建议且并不强求来发现这种循环,而且reject这个promise使用一个TypeError。github
思路都是最正常的思路,想要写一个Promise,确定得使用一个异步的函数,就拿setTimeout来作。数组
var p = new Promise(function(resolve){ setTimeout(resolve, 100); }); p.then(function(){console.log('success')},function(){console.log('fail')});
上面是个最简单的使用场景咱们须要慢慢来构建promise
function Promise(fn){ //须要一个成功时的回调 var doneCallback; //一个实例的方法,用来注册异步事件 this.then = function(done){ doneCallback = done; } function resolve(){ doneCallback(); } fn(resolve); }
下面加入链式,成功回调的方法就得变成数组才能存储异步
function Promise(fn){ //须要成功以及成功时的回调 var doneList = []; //一个实例的方法,用来注册异步事件 this.then = function(done ,fail){ doneList.push(done); return this; } function resolve(){ doneList.forEach(function(fulfill){ fulfill(); }); } fn(resolve); }
这里promise里面若是是同步的函数的话,doneList里面仍是空的,因此能够加个setTimeout来将这个放到js的最后执行。这里主要是参照了promiseA+的规范,就像这样函数
function resolve(){ setTimeout(function(){ doneList.forEach(function(fulfill){ fulfill(); }); },0); }
这时若是promise已经执行完了,咱们再给promise注册then方法就怎么都不会执行了,这个不符合预期,因此才会加入状态这种东西。更新过的代码以下学习
function Promise(fn){ //须要成功以及成功时的回调 var state = 'pending'; var doneList = []; //一个实例的方法,用来注册异步事件 this.then = function(done){ switch(state){ case "pending": doneList.push(done); return this; break; case 'fulfilled': done(); return this; break; } } function resolve(){ state = "fulfilled"; setTimeout(function(){ doneList.forEach(function(fulfill){ fulfill(); }); },0); } fn(resolve); }
如今的写法根本没有考虑异步返回的结果的传递,咱们来加上结果的传递
function resolve(newValue){ state = "fulfilled"; var value = newValue; setTimeout(function(){ doneList.forEach(function(fulfill){ value = fulfill(value); }); },0); }
这样子咱们就能够将then每次的结果交给后面的then了。可是咱们的promise如今还不支持promise的串行写法。好比咱们想要
var p = new Promise(function(resolve){ setTimeout(function(){ resolve(12); }, 100); }); var p2 = new Promise(function(resolve){ setTimeout(function(){ resolve(42); }, 100); }); p.then( function(name){ console.log(name);return 33; } ) .then(function(id){console.log(id)}) .then(p2) .then(function(home){console.log(home)});
因此咱们必须改下then方法。
当then方法传入通常的函数的时候,咱们目前的作法是将它推动了一个数组,而后return this来进行链式的调用,而且指望在resolve方法调用时执行这个数组。
最开始我是研究的美团工程师的一篇博客,到这里的时候发现他的解决方案比较跳跃,因而我就按照普通的正常思路先尝试了下:
若是传入一个promise的话,咱们先尝试继续推入数组中,在resolve的地方进行区分,发现是可行的,我先贴下示例代码,而后会有详细的注释。
function Promise(fn){ //须要成功以及成功时的回调 var state = 'pending'; var doneList = []; this.then = function(done){ switch(state){ case "pending": doneList.push(done); return this; break; case 'fulfilled': done(); return this; break; } } function resolve(newValue){ state = "fulfilled"; setTimeout(function(){ var value = newValue; //执行resolve时,咱们会尝试将doneList数组中的值都执行一遍 //当遇到正常的回调函数的时候,就执行回调函数 //当遇到一个新的promise的时候,就将原doneList数组里的回调函数推入新的promise的doneList,以达到循环的目的 for (var i = 0;i<doneList.length;i++){ var temp = doneList[i](value) if(temp instanceof Promise){ var newP = temp; for(i++;i<doneList.length;i++){ newP.then(doneList[i]); } }else{ value = temp; } } },0); } fn(resolve); } var p = function (){ return new Promise(function(resolve){ setTimeout(function(){ resolve('p 的结果'); }, 100); }); } var p2 = function (input){ return new Promise(function(resolve){ setTimeout(function(){ console.log('p2拿到前面传入的值:' + input) resolve('p2的结果'); }, 100); }); } p() .then(function(res){console.log('p的结果:' + res); return 'p then方法第一次返回'}) .then(function(res){console.log('p第一次then方法的返回:'+res); return 'p then方法第二次返回'}) .then(p2) .then(function(res){console.log('p2的结果:' + res)});
我按照正常思路这么写的时候发现出了点问题,由于按照最上面的规范。即便一个promise被rejected,他注册的then方法以后再注册的 then方法会可能继续执行resolve的。即咱们在then方法中为了链式返回的this的status是可能会被改变的,假设咱们在实现中来改变状 态而不暴露出来(这其实一点也不推荐)。
我直接贴实现的代码,还有注释做为讲解
function Promise(fn){ var state = 'pending'; var doneList = []; var failList= []; this.then = function(done ,fail){ switch(state){ case "pending": doneList.push(done); //每次若是没有推入fail方法,我也会推入一个null来占位 failList.push(fail || null); return this; break; case 'fulfilled': done(); return this; break; case 'rejected': fail(); return this; break; } } function resolve(newValue){ state = "fulfilled"; setTimeout(function(){ var value = newValue; for (var i = 0;i<doneList.length;i++){ var temp = doneList[i](value); if(temp instanceof Promise){ var newP = temp; for(i++;i<doneList.length;i++){ newP.then(doneList[i],failList[i]); } }else{ value = temp; } } },0); } function reject(newValue){ state = "rejected"; setTimeout(function(){ var value = newValue; var tempRe = failList[0](value); //若是reject里面传入了一个promise,那么执行完这次的fail以后,将剩余的done和fail传入新的promise中 if(tempRe instanceof Promise){ var newP = tempRe; for(i=1;i<doneList.length;i++){ newP.then(doneList[i],failList[i]); } }else{ //若是不是promise,执行完当前的fail以后,继续执行doneList value = tempRe; doneList.shift(); failList.shift(); resolve(value); } },0); } fn(resolve,reject); } var p = function (){ return new Promise(function(resolve,reject){ setTimeout(function(){ reject('p 的结果'); }, 100); }); } var p2 = function (input){ return new Promise(function(resolve){ setTimeout(function(){ console.log('p2拿到前面传入的值:' + input) resolve('p2的结果'); }, 100); }); } p() .then(function(res){console.log('p的结果:' + res); return 'p then方法第一次返回'},function(value){console.log(value);return 'p then方法第一次错误的返回'}) .then(function(res){console.log('p第一次then方法的返回:'+res); return 'p then方法第二次返回'}) .then(p2) .then(function(res){console.log('p2的结果:' + res)});
这篇文章是本身根据比较正常的思路来写的一个简单的promise。
接下来还会有篇文章来自习研究下美团那篇博客以及一些主流的promise的写法,敬请期待。
参考:
先写到这里,顺便给个github的传送门,喜欢的朋友star一下啊,本身平时遇到的问题以及一下学习的经历及写代码的思考都会在github上进行记录~