JS-原生/对于promise对象以及异步机制的理解

来源:互联网 发布:自学绘画知乎 编辑:程序博客网 时间:2024/05/16 01:54

promise是ES6新增的语法,他的出现极大地改善了异步编程中“回调地狱”的情况,promise究竟有什么用呢?在踩了很多坑之后,我总结出了一套自己对于promise的理解,本文争取用一段最简单的代码,让新手秒懂promise的用法,老鸟轻拍!

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

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

很显然,a是undefined,因为js是非阻塞的,它不会等你定时器里的东西走完了,再执行打印a这个操作。

它不等你读完,就会迫不及待地去打印a,因为a要1秒后才能被赋值,所以此时a就像一个早产儿一样,只是个undefined。

那怎么办?很简单,把【打印a】这个操作也放进延时定时器里嘛:

setTimeout(function (){a = 10;console.log(a);//成功打印为10},1000);

我想上一个例子并不难理解,那么接下来我会试图将其功能进行拓展,【打印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构造函数,从形式上改善了回调地狱。

上述打印a的例子可以改为:

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

看似很复杂,实际上只要记住一点,resolve对应的是then的第一个回调函数,reject对应的是then的第二个回调函数,原本回调版的代码顺序咋写的,promise还是咋写,只不过把异步执行(即需要等待数据的部分,比如我那个打印a,或者取一张img的width)的部分写进then的回调里,然后把resolve当作这个位于then里的回调,在老地方进行调用就行了(reject同理)

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

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


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

getUp(function (){wearClothes(function (){brushTeeth(function (){washFace(function (){eatBreakfast(function (){goToCompany()})})})})})


这样的形式相当恶心,相当难以维护,好在我们有promise:

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并没有改变什么,只是把异步代码的书写形式从水平方向改为了竖直方向,让代码更符合人类的阅读习惯

关于promise的初步入门就先到这,若有错误欢迎指出。


原创粉丝点击