「JavaScript」同步、异步、回调执行顺序之经典闭包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分析
今天同学问了我一个问题,我一看是一道经典的面试题,问题如下:

简单的这个问题改一下:
for (var i = 0; i <= 5; i++) {
setTimeout(function() {
console.log( i );
}, i*1000);
console.log( ' i : ' , i );
}
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语法了,
for (let i = 0; i <= 5; i++) {
setTimeout(function() {
console.log( i );
}, i*1000);
console.log( ' 1 : ' , i );
}
console.log( i );
我们都知道es5中变量作用域是函数,而es6却可以使用let声明一个具有块级作用域的i,在这里也就是for循环体;
在这里let本质上就是形成了一个闭包,那么写成es5的形式其实等价于:
var loop = function (_i) {
setTimeout(function() {
console.log( _i);
}, _i*1000);
console.log('2:',_i)
6 };
8 for (var _i = 0; _i <= 5; _i++) {
9 loop(_i);
}
总结
到这里,我们就完成了从同步、异步、回调的机制分析 到 setTimeout的经典案例的分析,JavaScript博大精深,我们需要了解他的机制去深入去挖掘他。
「JavaScript」同步、异步、回调执行顺序之经典闭包setTimeout分析的更多相关文章
- 同步、异步、回调执行顺序之经典闭包setTimeout分析
聊聊同步.异步和回调 同步,异步,回调,我们傻傻分不清楚, 有一天,你找到公司刚来的程序员小T,跟他说:“我们要加个需求,你放下手里的事情优先支持,我会一直等你做完再离开”.小T微笑着答应了,眼角却滑 ...
- js同步-异步-回调
出处:https://blog.csdn.net/u010297791/article/details/71158212(1)上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步.异步.回调的 ...
- C# 同步 异步 回调 状态机 async await Demo
源码 https://gitee.com/s0611163/AsyncAwaitDemo 为什么会研究这个? 我们项目的客户端和服务端通信用的是WCF,我就想,能不能用异步的方式调用WCF服务呢?或者 ...
- 「JavaScript」四种跨域方式详解
超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript 的同源策略,并且了解使用跨域跨域的理由. 1. JSONP 首先要介绍的跨域方法必然是 JSON ...
- 「JavaScript」JS四种跨域方式详解
原文地址https://segmentfault.com/a/1190000003642057 超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript ...
- javascript的预编译和执行顺序
原文:javascript的预编译和执行顺序 最近在复习javascript的事件处理时发现了一个问题,然后也是我来写javascript的预编译和执行顺序的问题 代码: 代码一<html> ...
- JavaScript在页面中的执行顺序(理解声明式函数与赋值式函数) 转载
JavaScript在页面中的执行顺序 https://blog.csdn.net/superhoy/article/details/52946277 2016年10月27日 15:38:52 阅读数 ...
- 浅谈个人对客户端JavaScript同步、异步、执行顺序等概念的理解
一.同步和异步的概念. 同步:即按代码的顺序执行任务. 在下列代码中,按照同步概念,则是先打印1后打印2. console.log(1); console.log(2); 异步:即执行一个任务的同时执 ...
- 前端笔记之JavaScript(九)定时器&JSON&同步异步/回调函数&函数节流&call/apply
一.快捷位置和尺寸属性 DOM已经提供给我们计算后的样式,但是还是觉得不方便,因为计算后的样式属性值都是字符串类型. 不能直接参与运算. 所以DOM又提供了一些API:得到的就是number类型的数据 ...
随机推荐
- 【前端】深入浅出Javascript中的数值转换
由于Javascript是一门弱类型的语言,在我们的代码中无时无刻不在发生着类型转换,所以了解Javascript中的类型转换对于了解我们认识Javascript的运行原理至关重要. 本文主要从数值转 ...
- hdu1754线段树的单点更新区间查询
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- 决策树模型组合之随机森林与GBDT
版权声明: 本文由LeftNotEasy发布于http://leftnoteasy.cnblogs.com, 本文可以被全部的转载或者部分使用,但请注明出处,如果有问题,请联系wheeleast@gm ...
- 移动HTNL5前端框架—MUI
前 言 JRedu 鉴于之前的很多前端框架(特别是响应式布局的框架),UI控件看起来太像网页,没有原生感觉,因此追求原生UI也是MUI的重要目标.MUI以iOS平台UI为基础,补充部分Andro ...
- 初学者易上手的SSH-struts2 01环境搭建
首先,SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是目前较流行的一种Web应用程序开源集成框架,用于构建灵活.易于扩展的多层Web应用程序. 集成SSH框 ...
- PWA学习心得
PWA学习心得 一.什么是PWA Progressive Web App , (渐进式增强 WEB 应用) 简称 PWA ,是提升WebApp的体验的一种新方法,能给用户原生应用的体验. PWA ...
- Linux软件安装管理
1.软件包管理简介 1.软件包分类 源码包 脚本安装包 二进制包(RPM包.系统默认包) 2.源码包 源码包的优点是: 开源,如果有足够的能力,可以修改源代码 可以自由选择所需要的功能 软件设计编译安 ...
- 多线程(RunLoop)
1.RunLoop的概念及作用 2.RunLoop的使用 3.RunLoop的相关类 4.RunLoop的工作原理 5.小结 6.思考 什么是RunLoop? 从字面意思上是一直循环跑,事实上就是一个 ...
- JS代码中!!的用法,以及代码性能对比
一.!!的理解 解释: !!的意思就是把一个任意类型的值转换为布尔类型的值,一个!是取非 再一个!又取非,相当于把这个数据转换为boolen类型了. 使用场景: 常常用在if(a).if(!!a)这样 ...
- Python基础2 编码和逻辑运算符
编码: AscII码 :标准ASCII码是采用7位二进制码来编码的,当用1个字节(8位二进制码)来表示ASCII码时,就在最高位添加1个0. 一个英文字母占一个字节 8位(bit)==一个字节(byt ...