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

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

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

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

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

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

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

JavaScript单线程

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

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

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

  • 同步和异步任务分别进入不一样的执行环境,同步的进入主线程,异步的写入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你们并不陌生,在工做中可能也会用到,异步函数,还能延迟执行~promise

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');bash

  • 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 new Promise(function (resolve, reject) {
            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对应的回调函数,当即执行。
  • 执行结束。

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

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

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,子晓),以为写的很不错,这里也仿照着来记录一下,也加了一下本身见解,但愿能给更多的人带来帮助~~~

相关文章
相关标签/搜索