同步,异步,回调,咱们傻傻分不清楚,node
有一天,你找到公司刚来的程序员小T,跟他说:“咱们要加个需求,你放下手里的事情优先支持,我会一直等你作完再离开”。小T微笑着答应了,眼角却滑过一丝不易觉察的杀意。程序员
世界上的全部事情大体能够分为同步去作和异步去作两种。你打电话去订酒店,电话另外一边的工做人员须要查下他们的管理系统才能告诉你有没有房间。es6
这时候你有两种选择:一种是不挂电话一直等待,直到工做人员查到为止(可能几分钟也可能几个小时,取决于他们的办事效率),这就是同步的。面试
另外一种是工做人员问了你的联系方式就挂断了电话,等他们查到以后再通知你,这就是异步的,这时候你就能够干点其余事情,好比把机票也定了之类的ajax
计算机世界也是如此,咱们写的代码须要交给cpu去处理,这时候就有同步和异步两种选择vim
js是单线程的,若是全部的操做(ajax,获取文件等I/O操做<node>)都是同步的,遇到哪些耗时的操做,后面的程序必然被阻塞而不能执行,页面也就失去了响应,闭包
所以js采用了事件驱动机制,在单线程模型下,使用异步回调函数的方式来实现非阻塞的IO操做,异步
那么什么是异步任务呢?(参考阮一峰老师《JavaScript运行机制》)函数
异步任务也就是 指主线程(stack栈)运行的过程当中,当stack空闲的时候,主线程对event queque(队列)
轮询(事实上一直在轮询)后,将异步任务放到stack
里面进行执行;oop
(上图转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》))
简单的说,若是咱们指定过回调函数,那么当事件发生时就会进入事件队列,等待主线程的(stack)空闲的时候,就会对event queue
里面的回调读取并放到stack里面执行
咱们常常说的多是异步回调(固然也有同步回调),因此也就并不难理解,回调和异步之间其实并无直接的联系,回调只是异步的一种实现方式,
经过这样的event loop咱们其实能够分析出三者的执行顺序,即 同步 > 异步 > 回调
今天同窗问了我一个问题,我一看是一道经典的面试题,问题以下:
简单的这个问题改一下:
1 for (var i = 0; i <= 5; i++) { 2 setTimeout(function() { 3 console.log( i ); 4 }, i*1000); 5 console.log( ' i : ' , i ); 6 } 7 8 console.log( i );
相信咱们不少人都遇到过这个问题,心中或许都有答案:
那么为何并非入门者心中所想要的结果嘞(为何setTimeout中打印出i所有是6,并且是最后才打印出来呢)?
那么就让咱们来梳理一下,第一部分event loop图片很直观的体现:"任务队列"能够放置异步任务的事件,也能够放置定时事件(setTimeout和setinterval),即指定某些代码在多少时间以后执行;
一、首先咱们先来看一下他的主体结构: for循环的第一层是setTimeout函数,setTimeout函数中使用了一个匿名(回调)函数
二、还记的咱们以前总结的执行顺序:同步 > 异步 > 回调 吧!
1)for循环和外层的 console.log()是同步的,setTimeout是回调执行,
因此按照执行顺序,先执行for循环,而后进入for循环中,他发现了一个setTimeout()回调(进入event queque事件队列,等待stack栈为空后读取并放入栈中后执行),这时候他并不会等待
而是继续执行 --> for循环内部的 console.log( ' i : ' , i ) --> for循环外部的console.log( i ) ,而后"任务队列"中的回调函数才进入到空Stack中开始执行;
咱们在来用这个例子尝试一下上面的event loop图,更加直观的感觉一下:
那么接下来可能会问怎么解决这个问题呢?我想最简单的固然是let语法了,
1 for (let i = 0; i <= 5; i++) { 2 setTimeout(function() { 3 console.log( i ); 4 }, i*1000); 5 console.log( ' 1 : ' , i ); 6 } 7 8 console.log( i );
咱们都知道es5中变量做用域是函数,而es6却可使用let声明一个具备块级做用域的i,在这里也就是for循环体;
在这里let本质上就是造成了一个闭包,那么写成es5的形式其实等价于:
1 var loop = function (_i) { 2 setTimeout(function() { 3 console.log( _i); 4 }, _i*1000); 5 console.log('2:',_i)
6 };
7
8 for (var _i = 0; _i <= 5; _i++) {
9 loop(_i);
10 }
到这里,咱们就完成了从同步、异步、回调的机制分析 到 setTimeout的经典案例的分析,JavaScript博大精深,咱们须要了解他的机制去深刻去挖掘他。