ES6中Promise可以说很大情况下改善了异步回调的嵌套问题,那么如果我们自己去写一个类似Promise的库应该怎么去写?
我们先看一下Promise的特点:
第一:Promise构造函数接受一个函数作为参数,函数里面有两个参数resolve和reject分别作为执行成功或者执行失
第二:可以通过then设置操作成功之后的操作,接受两个函数作为参数
那么原生js实现以上特点应该比较简单了
以上只是最基本的实现,在代码结构结构和容错方面没有进行考虑。
对ES6里promise对象以及异步机制的理解
我现在声明一个a,并且要延时1秒钟后给他赋值为10,然后打印这个a:
很显然,a是undefined,因为js是非阻塞的,它不会等你定时器里的东西走完了,再执行打印a这个操作。它不等你读完,就会迫不及待地去打印a,因为a要1秒后才能被赋值,所以此时a只是个undefined。
那怎么办?很简单,把打印a这个操作也放进延时定时器里嘛,我想这个例子并不难理解,那么接下来我会试图将其功能进行拓展,打印a这个需求我并不需要,我想要用a做其他事情,比如让他乘以2并输出返回结果,或者其他的涉及到a的操作。那很简单,我将console.log(a)这段代码删去,封装成一个函数,并将其作为参数传进这个定时器里,也就是所谓的“回调函数”如下:
调用foo的时候,作为参数的这个匿名函数,就是所谓的回调函数,回调函数这个机制极大地增加了代码的可能性,你可以在回调中做任何事,并且在不同的地方进行调用,你也将得到不同的反馈,如果将一个函数执行过程看成一个时间轴,那回调函数就可以允许你在这个过程中的各个时刻内执行其他代码,例如你可以将Vue.js的生命周期钩子函数看成是不同时期的回调函数
回调函数的作用显而易见,但如果一层回调套一层回调的话,代码极其不方便阅读与修改,因此ES6提供了Promise构造函数,从形式上改善了回调地狱。
现在我们就以一个人起床后的行为为例来深入理解异步执行机制,来看代码:
因为js是异步执行的,所以如果这么写,逻辑上等同于一个人同时开始做“起床+穿衣+刷牙+洗脸+吃早饭+出门上班”这些事,显然是不可能的事情,与我们想要的结果大相径庭,依照回调函数的写法,我们会在上述每个行为结束之后(即函数执行完毕的我们想要让他做什么,例如$.ajax()的success)传入一个回调函数,并将下一步的函数写进回调里,环环相扣,写法比较恶心,相当难以维护。
如果要链式调用then的话,必须要注意的一点是,一定要在then里的回调函数return一个新的promise对象,否则执行顺序将向上开始查找,直到找到最近的promise实例。
写到这里大家会发现promise并没有改变什么,只是把异步代码的书写形式从水平方向改为了竖直方向,让代码更符合人类的阅读习惯
我们先看一下Promise的特点:
第一:Promise构造函数接受一个函数作为参数,函数里面有两个参数resolve和reject分别作为执行成功或者执行失
[JavaScript]
纯文本查看
复制代码
1 2 3 4 5 6 7 8 | var promise=new Promsie(function(resolve,rejec){if(/*异步执行成功*/){resolve(value);}else{reject(error);}}) |
第二:可以通过then设置操作成功之后的操作,接受两个函数作为参数
[JavaScript]
纯文本查看
复制代码
1 2 3 4 5 | .then(function(){//回调执行成功之后的操作},function(){//回调执行失败之后的操作,可以没有}); |
那么原生js实现以上特点应该比较简单了
[JavaScript]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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.msgconsole.log("ok!");},function(){console.log('fail!');});//123//ok |
以上只是最基本的实现,在代码结构结构和容错方面没有进行考虑。
对ES6里promise对象以及异步机制的理解
我现在声明一个a,并且要延时1秒钟后给他赋值为10,然后打印这个a:
[JavaScript]
纯文本查看
复制代码
1 2 3 4 5 | var a;setTimeout(function (){a = 10;},1000);//1000ms后给a赋值为10console.log(a)//undefined, |
很显然,a是undefined,因为js是非阻塞的,它不会等你定时器里的东西走完了,再执行打印a这个操作。它不等你读完,就会迫不及待地去打印a,因为a要1秒后才能被赋值,所以此时a只是个undefined。
那怎么办?很简单,把打印a这个操作也放进延时定时器里嘛,我想这个例子并不难理解,那么接下来我会试图将其功能进行拓展,打印a这个需求我并不需要,我想要用a做其他事情,比如让他乘以2并输出返回结果,或者其他的涉及到a的操作。那很简单,我将console.log(a)这段代码删去,封装成一个函数,并将其作为参数传进这个定时器里,也就是所谓的“回调函数”如下:
[JavaScript]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 | 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构造函数,从形式上改善了回调地狱。
[JavaScript]
纯文本查看
复制代码
1 2 3 4 5 6 7 8 9 | var a;var p = new Promise(function (resolve,reject){ setTimeout(function (){ a = 10; resolve();/*reject也就是失败时对应的函数,由于这个例子比较简单,就不再赘述,毕竟是面向新手的*/ },1000)}).then(function (){ console.log(a)}) |
现在我们就以一个人起床后的行为为例来深入理解异步执行机制,来看代码:
[JavaScript]
纯文本查看
复制代码
1 2 3 4 5 6 | getup();wearClothes();brushTeeth();washFace();eatBreakfast();goToCompany(); |
因为js是异步执行的,所以如果这么写,逻辑上等同于一个人同时开始做“起床+穿衣+刷牙+洗脸+吃早饭+出门上班”这些事,显然是不可能的事情,与我们想要的结果大相径庭,依照回调函数的写法,我们会在上述每个行为结束之后(即函数执行完毕的我们想要让他做什么,例如$.ajax()的success)传入一个回调函数,并将下一步的函数写进回调里,环环相扣,写法比较恶心,相当难以维护。
[JavaScript]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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并没有改变什么,只是把异步代码的书写形式从水平方向改为了竖直方向,让代码更符合人类的阅读习惯