JavaScript 同步和异步(执行机制)
栏目: JavaScript · 发布时间: 5年前
内容简介:首先我放在文章的开头,重点说两点:好了,那么我们开始这篇文章的主要内容,既然说js 是单线程,那就是在执行代码的时候是从上往下执行的,我们来看一段代码吧:看到上面的代码,可能大家给出的答案是:'定时器开始','Promise开始','执行then函数','代码执行结束'。那么我们验证一下,输出结果却是:
首先我放在文章的开头,重点说两点:
- JavaScript 是一门单线程语言。
- 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的任务也是一个一个执行,如果前面一个用时很长,那后面哪一个就得等着,等到前面的执行完再继续。现在我们在打开某一个网站的时候,通常会看到有些图片加了背景图,因为页面中有些图片,视频,大批量的数据非常耗时,如果网速不好的电脑去打开,那想而知,空白啦~所以有了现在的 同步 和 异步!~
- 同步和异步任务分别进入不同的执行环境,同步的进入主线程,异步的写入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对应的回调函数,立即执行。
- 执行结束。
事件循环,宏任务,微任务的关系如图(这里看到很多作者用到的一张图,我也那过来吧,谢谢图片作者):
那下面就拿到一段代码了,在网上也能看到:
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,子晓),觉得写的很不错,这里也仿照着来记录一下,也加了一下自己看法,希望能给更多的人带来帮助~~~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。