JavaScript 同步和异步(执行机制)

栏目: JavaScript · 发布时间: 5年前

内容简介:首先我放在文章的开头,重点说两点:好了,那么我们开始这篇文章的主要内容,既然说js 是单线程,那就是在执行代码的时候是从上往下执行的,我们来看一段代码吧:看到上面的代码,可能大家给出的答案是:'定时器开始','Promise开始','执行then函数','代码执行结束'。那么我们验证一下,输出结果却是:

首先我放在文章的开头,重点说两点:

  1. JavaScript 是一门单线程语言。
  2. Event Loop(事件循环)是JavaScript 的执行机制。

好了,那么我们开始这篇文章的主要内容,既然说js 是单线程,那就是在执行代码的时候是从上往下执行的,我们来看一段代码吧:

setTimeout(function(){
    console.log('定时器开始')
});
new Promise(function(resolve){
    console.log('Promise开始');
    resolve();
}).then(function(){
    console.log('执行then函数')
});
console.log('代码执行结束');
复制代码

看到上面的代码,可能大家给出的答案是:'定时器开始','Promise开始','执行then函数','代码执行结束'。那么我们验证一下,输出结果却是:

  • Promise开始
  • 代码执行结束
  • 执行then函数
  • 定时器开始

JavaScript单线程

看到这样的答案,我们还是研究一下他的的执行机制吧,至于预解析我后面会单独发一篇文章单独说,这里咱们就主要看后面的执行机制了,我们说JavaScript是一个单线程语言,那他的 "多线程" 是咋来的,都是模拟出来的,一会我们来看看这个"多线程的纸老虎"。

JavaScript 同步异步(初探事件循环)

既然是单线程,那么我们可以想到,比如去银行办理业务,到了那里要去排队一个一个办理,同样的JavaScript的任务也是一个一个执行,如果前面一个用时很长,那后面哪一个就得等着,等到前面的执行完再继续。现在我们在打开某一个网站的时候,通常会看到有些图片加了背景图,因为页面中有些图片,视频,大批量的数据非常耗时,如果网速不好的电脑去打开,那想而知,空白啦~所以有了现在的 同步 和 异步!~

JavaScript 同步和异步(执行机制)
  • 同步和异步任务分别进入不同的执行环境,同步的进入主线程,异步的写入Event Table事件列表中。
  • 当事件完成时,把事件列表中的任务推入Event queue 事件队列,等待执行。
  • 主线程完成所有任务,就去查看事件队列里面,有的话拿出来执行。
  • 上面这个步骤会重复执行,知道没有可执行的任务,形成事件循环(Event Loop);
axios.post('/user', { name: 'fly', age: '30' })
.then(function (response) {
    console.log(response);
 })
console.log('代码执行结束');
复制代码
  • axios 进入事件列表。开始执行主线程,遇到console.log("代码执行结束");
  • 然后axios执行完毕,回到函数进入事件队列 Event Queue;
  • 主线程没有任务了去事件队列里面去拿,拿到了回调函数执行;

setTimeout

相信setTimeout大家并不陌生,在工作中可能也会用到,异步函数,还能延迟执行~

setTimeout(() => {
    console.log('执行setTimeout');
},3000)
console.log('执行console');
复制代码

按照我们前面的思路,先执行console.log('执行console');然后执行console.log('执行setTimeout');答案没错,那再变换一下:

setTimeout(() => {
    abc();
},3000)
exercise(100000000);//假设有个运动函数,需要时间很长...而且不固定...大于3秒,可能10秒
复制代码

那么我们在运行结果,发现3秒后并没有执行console.log('执行setTimeout');

  • setTimeout 异步函数推到异步任务中,写入事件列表。
  • 主程序执行exercise函数,1秒,2秒,3秒.......等待中。
  • 三秒时间到,abc() 推入事件队列Event Queue,等待执行,但是exercise函数还没执行完,那就等着吧~
  • 终于执行完了,这是再去事件队列去找要执行的任务,abc()进入主线程,执行。

上面的流程走完,我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例中为abc())加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于3秒。有时候我们会看到这样的形式setTimeout(function(),0); 0秒是不是立即就执行了呢?其实并不是

那他的意识是说,不需要多少秒进入Event Queue ,待主线程任务走完,就回去执行。 还是刚才的例子:

setTimeout(() => {
    abc();
},0)
console.log('执行console');
//  1.执行console   2.abc()
复制代码

这里说一下,其实看到有笔者说,setTimeout有最小时间间隔限制,HTML5标准为4ms,小于4ms按照4ms处理,但是每个浏览器实现的最小间隔都不同。 setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒。这里先记下吧。

setInterval

两个定时器兄弟,原理一样,只不过setInterval会每隔指定的时间将注册的函数置入Event Queue。如果前面的任务时间过长,也会等待,不过这里注意一点,如果其他的函数执行时间过长,定时器会执行完毕会把这些任务从时间列表拿到事件队列,如果时间唱的函数执行后,超出了延时执行的时间,那么就会挨个的拿到主程序执行,看不出延时时间了。

Promise

这里简单说一下Promise的作用,细节就不多说了(还需要总结好多...),Promise对象是用于异步操作的。在实际的开发中,我们是不是会遇到这种情况:

$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('第一个数据返回成功!');
        $.ajax({
            url:www.javascript.com,
            data:data,
            success:() => {
                console.log('第二个数据返回成功!');
                ...如果还有再继续
            }
        })
    }
})
复制代码

看到这段代码,应该不陌生吧,相信大家一定遇到过,"看着还不错,挺好的~~~"!我信你个gui,糟老头子坏得很~,那现在Promise派上用场了:

function abc(url,param){
    return new Promise(function (resolve, reject) {
        request(url, param, function(){
            resolve('数据请求成功了');
        }, reject);
    });
}
abc.then(function(data){
    console.log(date);//'第一个数据请求成功了';
    return request(url, param, function(){
                resolve('数据请求成功了');
           }, reject);
}).then(function(){
    console.log(date);//'第二个数据请求成功了';
});
复制代码

这里不过多说Promise 的用法了,继续我们的正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义:

  • 宏任务 包含整个script代码块,setTimeout,setInterval
  • 微任务 Promise,process.nextTick(这里先不做叙述)

不同类型的任务会进入对应的事件队列Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。 Promise和process.nextTick会进入相同的。

setTimeout(function() {
    console.log('setTimeout');
})
new Promise(function(resolve) {
    console.log('promise');
    resolve();
}).then(function() {
    console.log('then');
})
console.log('console');
复制代码

执行结果为:promise,console,then,setTimeout

  • 这段代码作为宏任务,进入主线程。
  • 先看到setTimeout,那么注册后把他放到宏任务事件队列Event Queue。
  • 接下来到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
  • 遇到console.log(),立即执行。
  • 到现在第一次的宏任务执行结束。那么得看看有没有微任务啊,有,then 里面有啊,拿出来执行。
  • 第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。
  • 执行结束。

事件循环,宏任务,微任务的关系如图(这里看到很多作者用到的一张图,我也那过来吧,谢谢图片作者):

JavaScript 同步和异步(执行机制)

那下面就拿到一段代码了,在网上也能看到:

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
附上输出结果:1,7,6,8,2,4,3,5,9,11,10,12
复制代码

在此要感谢网上看到的各位作者的文章(重点看到的csdn,子晓),觉得写的很不错,这里也仿照着来记录一下,也加了一下自己看法,希望能给更多的人带来帮助~~~


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图解设计模式

图解设计模式

结城浩 / 杨文轩 / 人民邮电出版社 / 2017-1-1 / CNY 79.00

原版连续畅销12年、重印25次! 194张图表 + Java示例代码 = 轻松理解GoF的23种设计模式 《程序员的数学》《数学女孩》作者结城浩又一力作 ◆图文并茂 194张图表(包括57张UML类图)穿插文中,帮助理解各设计模式 ◆通俗易懂 用浅显的语言逐一讲解23种设计模式,读完此书会发现GoF书不再晦涩难懂 ◆专业实用 编写了Java程序代码来......一起来看看 《图解设计模式》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具