老实说,写这篇文章的时候心里是有点压抑的,因为受到打击了,为什么?就 因为喜欢折腾不小心看到了这个"简单"的函数:

       for (var i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i)
            }, i * 1000);
        }
        console.log(i);
  什么?这不就是我很久之前看到的先打印一个5,再打印一个5,之后每隔一秒就打印一个5,直到打印完6个5的实现方法吗?那么问题来了,如果我要依次打印0,1,2,3,4,5的话我该怎么办,其实在这之前我就知道有这两个方法:一个是这样:
  function log(i){
   setTimeout(function(){
    console.log(i)
    },i*1000)
   };
  

  for (var i = 0; i < 5; i++) {
            log(i);
        }
        console.log(i);
   还有一个是这样:
  for(var i=0;i<5;i++){
    (function(e){
      setTimeout(function(){
       console.log(e)
      },i*1000);
    })(i);
   };
  console.log(i);
  不怕笑话,在这之前我是没搞懂这两个函数真正意义上的作用是用来干嘛的,只强迫自己这样记住这样修改就可以了,但是现在不行啊,我有强迫症啊!于是,我慢慢分析了一下,发现上面那段代码可以分离成这样:
  i=0时;满足条件;
  setTimeout(function(){
    console.log(i)
    },0*1000);
  

  i=1时;满足条件;
  setTimeout(function(){
    console.log(i)
    },1*1000);
  

i=2时;满足条件;
  setTimeout(function(){
    console.log(i)
    },2*1000);
  

i=3时;满足条件;
  setTimeout(function(){
    console.log(i)
    },3*1000);
i=4时;满足条件;
  setTimeout(function(){
    console.log(i)
    },4*1000);
i=5时,不满足条件,跳出循环,接着执行for循环后面的console.log(i),打印5;最后依次每秒打印5;
  真有意思,为什么setTimeout里面的console.log会是后于for循环外面的console.log执行呢?直到我认识到了这个单词=>"队列",队列又有宏任务队列(Macro Task)以及微任务队列(Micro Task)之分,在javascript中:
  1. macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

  2. micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver

  3. 上面函数的setTimeout就属于宏任务

在js中,事件循环的顺序是从script开始第一次循环,随后全局上下文进入函数调用栈,碰到macro-task就将其交给处理它的模块处理完之后将回调函数放进macro-task的队列之中,碰到micro-task也是将其回调函数放进micro-task的队列之中。直到函数调用栈清空只剩全局执行上下文,然后开始执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次执行macro-task中的一个任务队列,执行完之后再执行所有的micro-task,就这样一直循环。

这就是为什么setTimeout里面的console.log会是后于for循环外面的console.log执行,在函数执行上下文中,seiTimeout函数会被放到处理他的macro-task的队列之中,所以循环的时候setTimeout里面的function是不会被执行的,而是等到所有整体代码(非队列)跑完之后才会执行队列中的函数;写到这里,可能会有点懵逼,其实我也有点懵逼,哈哈哈!!

  为了加深理解,还可以试试在里面加入Promise,于是就有了这个:

(function copy() {
setTimeout(function() {console.log(4)}, 0);
new Promise(function executor(resolve) {
console.log(1);
for( var i=0 ; i<10000 ; i++ )
{
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
})()

解释一下=>
1.首先,script任务源先执行,全局上下文入栈。
2.script任务源的代码在执行时遇到setTimeout,作为一个macro-task,将其回调函数放入自己的队列之中。
3.script任务源的代码在执行时遇到Promise实例。Promise构造函数中的第一个参数是在当前任务直接执行不会被放入队列之中,因此此时输出 1 。
4.在for循环里面遇到resolve函数,函数入栈执行之后出栈,此时Promise的状态变成Fulfilled。代码接着执行遇到console.log(2),输出2。
5.接着执行,代码遇到then方法,其回调函数作为micro-task入栈,进入Promise的任务队列之中,此时Promise的then 里面的function回调函数跟setTimeout里面的function
回调函数有着异曲同工之意,都会被放到各自的任务队列中,
 直到函数上下文即script中所有的非队列代码执行完毕后再执行,而且微任务队列优先于宏任务队列被处理,
总体顺序为:上下文非队列代码>
微任务队列回调函数代码>宏任务队列回调函数代码
6.代码接着执行,此时遇到console.log(3),输出3。
7.输出3之后第一个宏任务script的代码执行完毕,这时候开始开始执行所有在队列之中的micro-task。then的回调函数入栈执行完毕之后出栈,这时候输出5
8.这时候所有的micro-task执行完毕,第一轮循环结束。第二轮循环从setTimeout的任务队列开始,setTimeout的回调函数入栈执行完毕之后出栈,此时输出4。
最后,为了加深理解,再上一段代码:
console.log('golb1');
setTimeout(function() {
console.log('timeout1');
new Promise(function(resolve) {
console.log('timeout1_promise');
resolve();
    setTimeout(function(){
      console.log('time_timeout')
    });  
}).then(function() {
console.log('timeout1_then')
})
setTimeout(function() {
console.log('timeout1_timeout1');
});
})
new Promise(function(resolve) {
console.log('glob1_promise');
resolve();
  setTimeout(function(){
     console.log('prp_timeout')
    });
}).then(function() { console.log('glob1_then') }) 
如果你的执行结果是:golb1=>glob1_promise=>
glob1_then=>timeout1=>timeout1_promise=>timeout1_then=>prp_timeout=>time_timeout=>timeout1_timeout1,
可能异步队列算是入门了吧!~~上面的代码看起来有点杂乱,
可能用asyns搭配await改造一下会更好,但是这或多或少是鄙人从setTimeout中得到的见解吧,有啥不对之处望指正:
另外,参考了一下别人的文章:https://zhuanlan.zhihu.com/p/26238030写的确实不错
 
 
    
  

从setTimeout看js函数执行的更多相关文章

  1. 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...

  2. 如何编写高质量的 JS 函数(1) -- 敲山震虎篇

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm7Xi7JxQ作者:杨昆 一千个读者,有一千个哈姆雷特. 此系列文 ...

  3. (转)在网页中JS函数自动执行常用三种方法

    原文:http://blog.sina.com.cn/s/blog_6f6b4c3c0100nxx8.html 在网页中JS函数自动执行常用三种方法 在网页中JS函数自动执行常用三种方法 在HTML中 ...

  4. js函数延迟执行

    function delay(value){ //全局变量保存当前值 window._myTempDalayValue = value; setTimeout(function(){ //延时之后与全 ...

  5. JS函数自动执行

    关于让网页中的JavaScript函数自动执行,方法就多洛,但是万变不离其宗,下面给大家介绍一下! 前提条件,网页中必须有JS函数代码,或者,使用文件导入的方法也行: 在HTML中的Head区域中,有 ...

  6. js函数整合队列顺序执行插件

    前言 在日常开发中,也许我们会遇到这样的一个问题.我们利用[发布订阅模式](如果不了解的可以直接访问此链接www.cnblogs.com/xiaoxiaokun- )去执行[发布]事件时,遇到函数内部 ...

  7. 从bind函数看js中的柯里化

    以下是百度百科对柯里化函数的解释:柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.概念太抽象,可能 ...

  8. 如何使js函数异步执行

    CallbacksCallbacks使用场景在哪里?在很多时候需要控制一系列的函数顺序执行.那么一般就需要一个队列函数来处理这个问题: function Aaron(List, callback) { ...

  9. Python利用PyExecJS库执行JS函数

      在Web渗透流程的暴力登录场景和爬虫抓取场景中,经常会遇到一些登录表单用DES之类的加密方式来加密参数,也就是说,你不搞定这些前端加密,你的编写的脚本是不可能Login成功的.针对这个问题,现在有 ...

随机推荐

  1. SQL基本查询_多表查询(实验三)

    SQL基本查询_多表查询(实验三) 题目要求(一) 针对emp.dept两表完成如下查询,并验证查询结果的正确性 使用显式内连接查询所有员工的信息,显示其编号.姓名.薪水.入职日期及部门名称: 使用隐 ...

  2. JAVA中文乱码之解决方案

    1.解决HTML页面的中文问题:为了使HTML页面很好的支持中文,在每个HTML页面的<head>标签内部增加(创建HTML页面自带) <head> <meta char ...

  3. Layui框架+PHP打造个人简易版网盘系统

    网盘系统   大家应该都会注册过致命的一些网盘~如百度云.百科介绍:网盘,又称网络U盘.网络硬盘,是由互联网公司推出的在线存储服务,服务器机房为用户划分一定的磁盘空间,为用户免费或收费提供文件的存储. ...

  4. oracle存储过程统计用户各表记录数

    declare v_tName varchar(50); v_sqlanalyze varchar(500); v_num number; v_sql varchar(500); cursor c1  ...

  5. 一次触摸,Android到底干了啥

    WeTest 导读 当我们在写带有UI的程序的时候,如果想获取输入事件,仅仅是写一个回调函数,比如(onKeyEvent,onTouchEvent-.),输入事件有可能来自按键的,来自触摸的,也有来自 ...

  6. 获取所有栈的信息,只有最上面的和最下面的,但是不能获取栈中间的activity信息

    直接在cmd窗口上输入 adb shell后,再输入dumpsys activity activities,可以看到所有的activity以及相关栈状态

  7. Android OOM异常解决方案

    一,什么是OOM异常: OOM(out of Memory)即内存溢出异常,也就是说内存占有量超过了VM所分配的最大,导致应用程序异常终止: 二,为什么会产生OOM异常呢? OOM异常是Android ...

  8. .NET之RabbitMQ学习笔记(二)-安装

    安装 1.安装erlang语言环境 因为rabbitmq是基于erlang进行开发,所以需要安装相应的依赖环境,学习中用到的erlang包下载地址:http://www.erlang.org/down ...

  9. 0e开头MD5值小结

    s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387 ...

  10. Java IO(1)基础知识——字节与字符

    正所谓怕什么来什么,这是知名的“墨菲定律”.Java基础涵盖各个方面,敢说Java基础扎实的人不是刚毕业的学生,就是工作N年的程序员.工作N年的程序员甚至也不敢人人都说Java基础扎实,甚至精通,往往 ...