结合例子理解Javascript的同步、异步、callback、seTimeout

2 篇文章 0 订阅

零、参考

下面两篇是尝试对异步有个了解,其实讲得还是有很多不清楚不规范的地方

https://www.cnblogs.com/sameen/p/7922627.html

【只看前言和浅谈异步,因为后面的异步原理讲得不是很清楚,然后看下面的文章理解浅谈异步里面的例子】

https://www.cnblogs.com/moxiaowohuwei/p/8438236.html

【第二点的例子看不懂也没关系...还有后面的浏览器内核线程图不太靠谱,毕竟消息队列和事件循环都没有...】


下面是对异步原理的深入探索(带质疑态度去看,不一定对。有些地方不理解可以先跳过,而且不同文章的解释是不统一的...):

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

【这个靠谱点,但讲得不够详细。】

https://segmentfault.com/a/1190000004322358

【这个讲得详细点,但是忽略了非异步的函数执行栈,消息队列和事件循环讲得不错,评论也可以看一下】

【结合上面两篇可以知道:函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。】

【其实最正确的答案可能要研究整个异步过程的源代码,但时间和难度都...,不过有一定的理解,加上知道如何利用异步实现自己想要的功能,已经很不错了】


异步的实现:http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html

【会发现都存在延时函数setTimeout()】

 

一、结合例子尝试理解异步

题目一:

function B(){
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
}


function A() {

    B();//在第0秒执行这句,估计在第3秒输出
    setTimeout("console.log('我是主函数1')", 1000);//可假设在0.0001秒执行这句,估计1.0001秒输出  
    setTimeout("console.log('我是主函数2')", 1000);//可假设在0.0002秒执行这句,估计1.0002秒输出
    setTimeout("console.log('我是主函数3')", 3000);//可假设在0.0003秒执行这句,估计3.0003秒输出
    
}

A();//题目一输出结果:主1、主2、回调、主3

题目一JS引擎处理过程解析:

1.语法分析,发现没有语法错误;

2.进行预编译,对变量声明进行处理,这里只有函数声明,就开辟空间存放函数B、A(对延时函数的处理还不太清楚)

3.开始解释执行,遇到A();需要执行

3.1把A()放进函数执行栈,JS主线程处理执行栈里面的函数A

3.2遇到函数B需要执行,压入执行栈,JS主线程处理执行栈栈顶的函数B

3.3遇到函数B里面的延时函数,发起异步请求,把异步任务交给工作线程(对于延时函数的话应该是交给了定时器线程)

3.4函数B处理完从执行栈弹出,主线程执行执行栈的新栈顶函数A;同时工作线程处理异步任务,让定时器至少等3秒,然后把延时函数里面的输出语句封装成消息,放在消息队列里面,等待主线程处理完执行栈的任务,再通过事件循环来取这里的消息。

3.5主线程执行函数A里面的三条异步发起请求、然后工作线程处理这三条异步任务,至少等待对应的设置时间,才把输出语句封装成消息放在消息队列

3.6主线程执行栈为空了,就通过事件循环逐一地去取消息,执行消息,取消息,执行消息...不断循环,根据延时函数设置时间的不同,消息队列的消息顺序为输出主1、主2、回调、主3

3.7所以最终的结果就是输出主1、主2、回调、主3

 

思考题:

如果在时间循环的过程中函数执行栈又加入了新的函数,例如点击了按钮输出10000个1,这时候的事件循环会被阻塞吗?

在题目一的基础上加入

<button onclick="C()">点击</button>

function C(){
  for(var i = 0 ; i<10000; i++)
  console.log('cccc')
}

 

答案:会阻塞的,当函数执行栈不为空了,就会让消息队列的事件循环停止,先让函数执行,再等函数栈为空了,再去执行消息队列里面剩下的消息。

 

 

题目二、三:

//定义主函数,回调函数作为参数
function A(callback) {
    
    callback();//题目二的输出是(10000)2、(10000)1、主1、主2、回调、主3
    setTimeout("console.log('我是主函数1')", 1000);
    setTimeout("console.log('我是主函数2')", 1000);
    setTimeout("console.log('我是主函数3')", 3000);
    for(var i = 0; i < 10000; i++) {
        console.log(1);//这里耗时一般超过三秒
    }
    // callback(); //题目三的输出是:(10000)1、(10000)2、主1、主2、主3、回调函数

}

//定义回调函数
function B(){
    for(var i = 0; i < 10000; i++) {
        console.log(2);
    }
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
    
}

// 调用主函数,将函数B传进去
A(B);

尝试去解释题目二、三的输出。

提示1:这里面的for循环是非异步的,所以一旦遇到就马上执行

提示2:可能有人有这样的误区:认为回调函数要等主函数执行完才处理。其实不是,只要回调函数写在主函数的前面,JS一旦解析到了也会执行的,所以题目二先输出了2,题目三先输出了1。因为往往在回调里面写的都是耗时间长的异步任务,到最后才看到结果,所以给人一种回调函数最后执行的错觉。

 

 

另一个小题目:https://www.cnblogs.com/smswei/p/5240065.html

【后面的立即函数讲的优点不清楚,值得再去查资料理解】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值