同步、异步、回调执行顺序之经典闭包setTimeout分析

聊聊同步、异步和回调

同步,异步,回调,我们傻傻分不清楚,

有一天,你找到公司刚来的程序员小T,跟他说:“我们要加个需求,你放下手里的事情优先支持,我会一直等你做完再离开”。小T微笑着答应了,眼角却滑过一丝不易觉察的杀意。

世界上的所有事情大致可以分为同步去做和异步去做两种。你打电话去订酒店,电话另一边的工作人员需要查下他们的管理系统才能告诉你有没有房间。

这时候你有两种选择:一种是不挂电话一直等待,直到工作人员查到为止(可能几分钟也可能几个小时,取决于他们的办事效率),这就是同步的。

另一种是工作人员问了你的联系方式就挂断了电话,等他们查到之后再通知你,这就是异步的,这时候你就可以干点其他事情,比如把机票也定了之类的

 计算机世界也是如此,我们写的代码需要交给cpu去处理,这时候就有同步和异步两种选择

js是单线程的,如果所有的操作(ajax,获取文件等I/O操作<node>)都是同步的,遇到哪些耗时的操作,后面的程序必然被阻塞而不能执行,页面也就失去了响应,

因此js采用了事件驱动机制,在单线程模型下,使用异步回调函数的方式来实现非阻塞的IO操作,

那么什么是异步任务呢?(参考阮一峰老师《JavaScript运行机制》)

异步任务也就是 指主线程(stack栈)运行的过程中,当stack空闲的时候,主线程对event queque(队列)轮询(事实上一直在轮询)后,将异步任务放到stack里面进行执行;

(上图转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》))

 简单的说,如果我们指定过回调函数,那么当事件发生时就会进入事件队列,等待主线程的(stack)空闲的时候,就会对event queue里面的回调读取并放到stack里面执行

我们经常说的可能是异步回调(当然也有同步回调),所以也就并不难理解,回调和异步之间其实并没有直接的联系,回调只是异步的一种实现方式, 

通过这样的event loop我们其实可以分析出三者的执行顺序,即 同步 > 异步 > 回调

经典闭包setTimeout分析

今天同学问了我一个问题,我一看是一道经典的面试题,问题如下:

简单的这个问题改一下:

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),即指定某些代码在多少时间之后执行;

 1、首先我们先来看一下他的主体结构: for循环的第一层是setTimeout函数,setTimeout函数中使用了一个匿名(回调)函数

 2、还记的我们之前总结的执行顺序:同步 > 异步 > 回调 吧!

  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博大精深,我们需要了解他的机制去深入去挖掘他。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IMWeb前端团队

Unix Pipes to Javascript Pipes

本文作者:IMWeb 杨文坚 原文出处:IMWeb社区 未经同意,禁止转载 Unix Pipes Unix管道扫描稿 ? 简单样例: $ ne...

196100
来自专栏程序员互动联盟

【专业技术】USB体系结构

近来基于MSC类协议做了一个模拟U盘实现USB读写功能的项目,看到一个对USB框架讲得不错的文章,这里转载过来,方便需要的人看看。当然USB协议是一个很庞大的工...

32560
来自专栏PPV课数据科学社区

如何使用Python对Instagram进行数据分析?

我写此文的目的在于展示以编程的方式使用Instagram的基本方法。我的方法可用于数据分析、计算机视觉以及任何你所能想到的酷炫项目中。 Instagram是最大...

29470
来自专栏哲学驱动设计

《OEA - 实体扩展属性系统 - 设计方案说明书》

    这篇设计文档是 12 月份写来参加公司的研发峰会的,自己倒是信心满满,不过最后还是没有入围。现在想想也没啥大用,所以贴出来,期待与园友交流。     文...

36870
来自专栏Crossin的编程教室

这段代码实例,见证了数万人入门编程

我的微信 Python 教程里有两个比较重要的代码实例,一个是前期的“猜数字”,这个例子演示了基本的输入输出、运算、类型、逻辑,之后的扩展又涉及到函数、数据结构...

13430
来自专栏Android群英传

实现 APK 保护时常见的坑和解决方案

10830
来自专栏工作积累

回答:这几天找工作遇到的面试题目

https://laravel-china.org/articles/9983/the-interview-questions-we-met-in-the-la...

3.3K100
来自专栏编程

淘宝iOS端圣诞节雪花实现分析

前言 1 我们产品在群里说淘宝APP有雪花了,于是我从被窝里掏出了手机看下,哇塞飘雪花了! 如图: ? 那我就分析一下他们的实现方式吧。 分析淘宝APP 2 2...

27870
来自专栏草根专栏

使用C# (.NET Core) 实现适配器模式 (Adapter Pattern) 和外观模式 (Facade Pattern)

本文的概念内容来自深入浅出设计模式一书 现实世界中的适配器(模式) ? 我带着一个国标插头的笔记本电脑, 来到欧洲, 想插入到欧洲标准的墙壁插座里面, 就需要用...

29660
来自专栏瓜大三哥

UVM(十一)之各种port

UVM(十一)之各种port UVM中内置了各种port,用于实现TLM级别的。 1. uvm_component之间的通信 如果要在两个uvm_compone...

26970

扫码关注云+社区

领取腾讯云代金券