据说某俄国特工经过九死一生偷到了NASA的太空火箭发射程序的源代码的最后一页,代码是 ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
为了清楚的让大家了解这个冷笑话,下面我将用一个小案例为大家讲述。
红黄绿灯demo
假设我们要制作一个红黄绿灯的小demo,我们首先会创建ul标签,在ul标签中插入三个子节点,并把这个子节点设置成三个圆,接着给父节点设置监听器,每隔一秒就给子节点换不同的颜色。
<body>
//创建一个ul标签并插入三个子节点
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</body>
border-radious:50%,意思是在我这个盒子里面每个顶角上都会形成一个圆,圆的宽和高分别就是我这个盒子宽和高的一半,之后再去掉四个顶角,这样我就给这个盒子加圆角了。一旦我们把外面的盒子设置成了正方形,四个内圆重叠在了一起,我们里面取得的圆就是个标准圆。
基本布局完之后,页面如下,三个完全相等的圆。
添加监听器
为了使这个demo还具有动画效果,我们还需要使用监听器。在父节点里每隔一秒我就给这个父节点添加一个类选择器名称,由于我之前写了三个类选择器,分别为stop,warn,pass,因此每隔一秒我只需要改变类选择器的名称就可以了。
const ul = document.querySelector('ul');
setTimeout(() => {
ul.className = 'stop';
setTimeout(() => {
ul.className = 'warn';
setTimeout(() => {
ul.className = 'pass';
}, 1000);
}, 1000);
}, 1000);
我们再来运行一下,这时候页面就顺利的动起来了,开心!
小bug
不知道你没有发现,这样运行的话我们只能循环一次,并没有达到我们理想的一直循环下去的状态,怎么办呢?简单啊,我再加几个监听不就可以了吗,好的,接下里我们就多加几个监听看看有什么变化。
const ul = document.querySelector('ul');
setTimeout(() => {
ul.className = 'stop';
setTimeout(() => {
ul.className = 'warn';
setTimeout(() => {
ul.className = 'pass';
setTimeout(() => {
ul.className = 'stop';
setTimeout(() => {
ul.className = 'warn';
setTimeout(() => {
ul.className = 'pass';
setTimeout(() => {
ul.className = 'stop';
setTimeout(() => {
ul.className = 'warn';
setTimeout(() => {
ul.className = 'pass';
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
再运行一下,貌似是成功了,能循环好几次,难道这就是我们理想的demo吗?不是的,因为上述的代码,你会发现有无数个回调函数,这就是传说中的 某俄国特工九死一生偷到了NASA太空火箭发射程序,结果是JS的回调地狱,你从左边侧着看是不是特别像一个火箭。
好了,到目前为止我们介绍的都是为了今天的重点Promise做的铺垫,接下来我们将正式进入重点Promise。
认识Promise
所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件。在前端编程中,我们经常会使用Promise对象来处理异步事件。
Promise对象的状态不受外界影响,它有三个状态(Pending, Fullfilled, Rejected),一旦它的状态改变就不会再变了。下面是Promise的响应流程。
简单点,Promise的响应过程可以划分为两个步骤,第一在进程中,第二成功了/失败了。
如果我们不执行异步操作,我们的页面就必须得到服务器的响应后才能做其他操作,这无疑浪费了很多资源。Promise不仅是异步编程的一种解决方案,它还能将异步操作以同步操作的流程表达出来。
下面的代码为一个Promise的实例
let p = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve();
}, 1000);
})
}
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数,由JavaScript引擎提供,不用自己部署。
在上面的匿名函数中我许下了一个Promise,时间是1s以后要干的事。1s以后Promise的状态为resolve()
,说明Promise对象的状态由 进行中 转变到 已成功 状态,接着就可以进行回调函数。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
})
.then(value => {
console.log(value);
})
// 2
// 1
上述代码便是一个完整的Promise对象的链式操作了,Promise then 是串行结构,而回调则是嵌套结构,每一个then结束后都将返回Promise,状态为Resolved,因此,我可以串联多个then,异步执行多个操作。上面的代码中,调用resolve(1)以后,后面的console.log(2)还会执行,并且会首先打印出来,这是因为立即Resolved的Promise是在本轮事件循环的末尾执行,总是晚于本次循环的同步任务。
使用Promise链式调用后的demo
连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现这种需求。见证奇迹的时刻:
在light函数中,我用了三次then,绑定了三个回调函数。
在 第一个then 中我给ul这个标签加了一个红灯的css规则,之后这个回调函数返回我的promise对象,因为我的Promise对象状态还是resolve(成功的),我还可以接着then,所以1s之后我又在 第二个then 中把ul标签改为黄灯的css规则,最后1s之后 第三个then,我再把ul标签改成绿灯的css规则。经过三个then后我就可以实现一次红绿灯的跳动了,间隔都是1s跳一次。
注意:then回调函数一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。(如果使用箭头函数,() => x 比 () => { return x; } 更简洁一些,但后一种保留 return 的写法才支持使用多个语句。)。
最后,如果想让它实现连续循环的效果,我们还可以添加一个监听器。
setInterval(() => {
light();
}, 3000);
这样我就实现了每隔一秒跳动一次,每隔三秒循环一次的效果了。相比于之前的嵌套结构,这个链式调用简便了很多。虽然都是同样的效果,但是Promise的链式调用会让你的代码简化很多,因此,为了不让自己掉入JS的回调地狱,Promise对象用起来吧!
总结
关于Promise的链式调用就介绍到这里,欢迎大家给予建议和交流,如果有问题可以在下方评论,看到了会及时回复哒!