原生JS实现Promise


  ES6中Promise可以说很大情况下改善了异步回调的嵌套问题,那么如果我们自己去写一个类似Promise的库应该怎么去写?

  我们先看一下Promise的特点:

  第一:Promise构造函数接受一个函数作为参数,函数里面有两个参数resolve和reject分别作为执行成功或者执行失败的函数

var promise=new Promsie(function(resolve,rejec){ if(/*异步执行成功*/){ resolve(value); }else{ reject(error); } })

  第二:可以通过then设置操作成功之后的操作,接受两个函数作为参数

.then(function(){ //回调执行成功之后的操作
},function(){ //回调执行失败之后的操作,可以没有
});

  那么原生js实现以上特点应该比较简单了

function PromiseM(){ this.status='pending'; this.msg=''; var process=arguments[0]; var that=this; process(function(){ that.status='resolve'; that.msg=arguments[0]; },function(){ that.status='reject'; that.msg=arguments[0]; }); return this; } PromiseM.prototype.then=function(){ if(this.status=='resolve'){ arguments[0](this.msg); } if(this.status=='reject'&&arguments[1]){ arguments[1](this.msg); } } //测试用例
var mm=new PromiseM(function(resolve,reject){ resolve('123');//123其实就是第二个arguments[0] });//上面的第一个arguments[0] mm.then(function(success){ console.log(success);//该success其实就是上面的this.msg console.log("ok!"); },function(){ console.log('fail!'); });
//123
//ok

  以上只是最基本的实现,在代码结构结构和容错方面没有进行考虑。

  对ES6里promise对象以及异步机制的理解

  我现在声明一个a,并且要延时1秒钟后给他赋值为10,然后打印这个a:

var a; setTimeout(function (){ a = 10; },1000);//1000ms后给a赋值为10
console.log(a)//undefined,

  很显然,a是undefined,因为js是非阻塞的,它不会等你定时器里的东西走完了,再执行打印a这个操作。它不等你读完,就会迫不及待地去打印a,因为a要1秒后才能被赋值,所以此时a只是个undefined。

  那怎么办?很简单,把打印a这个操作也放进延时定时器里嘛,我想这个例子并不难理解,那么接下来我会试图将其功能进行拓展,打印a这个需求我并不需要,我想要用a做其他事情,比如让他乘以2并输出返回结果,或者其他的涉及到a的操作。那很简单,我将console.log(a)这段代码删去,封装成一个函数,并将其作为参数传进这个定时器里,也就是所谓的“回调函数”如下:

var a; function foo(callBack){ setTimeout(function (){
    a = 10;     callBack
&&callBack()   },1000) } foo(function (){ //因为a是全局的,所以不需要将a传参进foo的回调中   return a*2 })

  调用foo的时候,作为参数的这个匿名函数,就是所谓的回调函数,回调函数这个机制极大地增加了代码的可能性,你可以在回调中做任何事,并且在不同的地方进行调用,你也将得到不同的反馈,如果将一个函数执行过程看成一个时间轴,那回调函数就可以允许你在这个过程中的各个时刻内执行其他代码,例如你可以将Vue.js的生命周期钩子函数看成是不同时期的回调函数

  回调函数的作用显而易见,但如果一层回调套一层回调的话,代码极其不方便阅读与修改,因此ES6提供了Promise构造函数,从形式上改善了回调地狱。

var a; var p = new Promise(function (resolve,reject){   setTimeout(function (){     a = 10;     resolve();/*reject也就是失败时对应的函数,由于这个例子比较简单,就不再赘述,毕竟是面向新手的*/   },1000) }).then(function (){   console.log(a) })

  现在我们就以一个人起床后的行为为例来深入理解异步执行机制,来看代码:

getup();
wearClothes();
brushTeeth();
washFace();
eatBreakfast();
goToCompany();

  因为js是异步执行的,所以如果这么写,逻辑上等同于一个人同时开始做“起床+穿衣+刷牙+洗脸+吃早饭+出门上班”这些事,显然是不可能的事情,与我们想要的结果大相径庭,依照回调函数的写法,我们会在上述每个行为结束之后(即函数执行完毕的我们想要让他做什么,例如$.ajax()的success)传入一个回调函数,并将下一步的函数写进回调里,环环相扣,写法比较恶心,相当难以维护。

var p = new Promise(function (resolve){ getUp(function (){ resolve(); }); }).then(function (){ return new Promise(function (resolve){ wearClothes(function (){ resolve(); }); }) }).then(function (){ return new Promise(function (resolve){ brushTeeth(function (){ resolve(); }); }) }).then(function (){ return new Promise(function (resolve){ washFace(function (){ resolve(); }); }) }).then(function (){ return new Promise(function (resolve){ eatBreakfast(function (){ resolve(); }); }) }).then(function (){ goToCompany() })

  如果要链式调用then的话,必须要注意的一点是,一定要在then里的回调函数return一个新的promise对象,否则执行顺序将向上开始查找,直到找到最近的promise实例。

  写到这里大家会发现promise并没有改变什么,只是把异步代码的书写形式从水平方向改为了竖直方向,让代码更符合人类的阅读习惯


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM