写在前面
最近看了两篇不错的文章,都是和定时器(setTimeout)有关,所以我想写一篇博客,把那两篇文章能够融合、整理一下。至于标题,参照UC震惊部取的,笑一笑就好了。
正文
话不多说,直接进入正题吧,看下面一段代码,结果会输出什么:
|
|
当然是输出0,1,2,3,4,5呀,这么简单的东西也敢问人?
好吧,我们再看看下面这段代码会输出什么:
|
|
其实如果你对定时器(setTimeout)有个基本的了解的话,你就会清楚的知道,会输出5,5,5,5,5,5,OK?
那我想问下,这六个5输出的状态是怎样的,比如在输出时中间的时间间隔关系是怎样的?
这里有三个选项:
A.5->5->5->5->5->5,每个5之间都会间隔1秒。
B.555555,所有5一起输出。
C.5->55555,第一个5输出,等待1秒后后面5个5一起输出。
好的,不卖关子,答案是C,这里面实际上就设计了比较多的知识点,比如同步和异步代码的区别,变量的作用域,闭包等概念,所以实际输出的时候,会先输出for循环外面的那个i,由于i经过了上面的for循环,所以此时i已经是5了,所以会先输出5,然后就是由于定时器的原因,for循环中定时器里有5个一样的console函数,它们都会等待一秒,然后会输出i,而这个时候的i,已经是循环之后的i了,所以会输出5,所以才会5个函数同时等待一秒后,5个5同时输出,这就是选C的原因。这就要求度js的定时器工作机制有基本的了解。
进阶
上面的代码,输出方式为5->55555,那么,如果我想上面代码的输出变为:5->01234,那么会怎样,这里就考验了同学对于闭包的熟悉程度,其代码如下:
|
|
这里就是运用了IIFE来解决闭包造成的问题,其实我们可以把代码解藕一下,将IIFE函数提出来,就变成了下面这样:
|
|
既然都到了这里,那么如果我想输出结果变成这样,5->0->1->2->3->4,代码应该怎么写?
|
|
这里其实已经考察了闭包的灵活运用了,假设把func函数中的形参去掉,变成这样,结果会是怎样的?
|
|
这样的话,定时器函数的内部就没有保持对外部i的引用,就会变成5->5->5->5->5。
好吧前面都是小大小闹,看看这里,假设定时器里面传入的不是函数,而是一个IIFE函数的话,结果会怎样?
|
|
这就变成了传入一个IIFE函数进去,setTimeout可以接受一个函数或者字符串作为参数,可是这里的IIFE函数,会怎样呢?所以这里的定时器此时就没有任何的作用了,所以会直接一下子输出01234,不会有定时器功能。
ES6 Promise
完了吗?还早呢,这全是回调函数,ES6中的Promise现在已经用得比较广泛了,好吧,现在将上面的代码采用Promise来改写一下,应该怎么写?
|
|
上面就是采用Promise来改写后的代码,这里考察了Promise的熟练运用和新技术的学习,虽然Promise不算什么新技术呢。。。
ES7 async/await
好吧,既然Promise不算什么新的技术,那我们来看看新的技术——acync/await函数。老规矩,将上面的异步代码用ES7中的async/await函数重写一下。
|
|
注意:await后面可以是一个异步函数也可以不是,我之前在sleep中的Promise忘了加上return,结果定时器就没有发生作用。。。无语。
好了ES6的Promise和ES7的async/await函数都讲到了,下面来看下最后一道题:
|
|
这道题其实初看,还是挺难的,异步就有promise和setTimeout,还有for循环来分散你的注意力,也是危机四伏啊。
其实没必要这么麻烦,我们可以先基本看一遍,知道其主要意思,就是让你去分一下异步与同步,同时还有两种异步来让你去分先后顺序;所以首先,我们需要来去分一下这段代码中的同步和异步先将同步与异步分开,首先来看,哪些是同步哪些是异步:按先后顺序来说,同步的有2,3,5;异步的是1和4。而且结果中,肯定是同步在前异步在后,所以结果应该是(2,3,5)(1,4),括号中的顺序可能会有改动,然后在来每个仔细分析一下,首先是同步,同步其实很简单,就是简单的按先后顺序输出,所以前面的2,3,5,顺序不用改变,这里有一些小陷阱,比如setTimeout的定时为0,其实这还是一个定时器,接下来就是有一个9999的循环,其实这个并不会影响什么,也不需要太在意这个;最后,再来看看异步,先是一个setTimeout的console.log(1),然后是Promise的回调console.log(4),假设是两个相同的异步,比如都是setTimeout或者都是Promise,那么也很简单,也将会是按先后顺序输出,现在问题来了,那么setTimeout和Promise这两种回调方法,有什么差异呢?如果在回调上没有差异,那么就是按先后顺序了,结果呢?Promise和setTimeout回调最大的区别在于,Promise的回调,会放在当前事件队列的末尾,但是setTimeout不是,它会放在下一个事件队列的头部,所以Promise比setTimeout会更快执行回调,所以是Promise的回调先输出,所以最终结果也就是2,3,5,4,1。