我们首先要知道JavaScript是一门单线程的语言,顾名思义”单线程”,就是指一次只能执行一个任务,如果有多个任务,那就必须排队执行,在上一个任务执行完毕之后,再去执行后面的任务,以此类推。如果一个任务耗时过长,那么后面的任务就必须等待这个耗时过长的任务完成,才能继续往下执行,那么这种情况会造成什么后果呢?拖延我们的程序执行,常见的浏览器无反应。于是,JavaScript将所有任务分为两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
Js是单线程语言,它是基于事件循环,事件循环大致分为以下几个步骤:
1.所有同步任务都在主线程上执⾏, 形成⼀个执⾏栈(execution context stack)。
2.主线程之外, 还存在⼀个”任务队列“(task queue) 。只要异步任务有了运⾏结果, 就在”任务队列”之中放置⼀个事件。
3.一旦”执⾏栈”中的所有同步任务执⾏完毕, 系统就会读取“任务队列”, 看看⾥⾯有哪些事件。那些对应的异步任务, 于是结束等待状态, 进⼊执⾏栈, 开始执⾏。
同步任务
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务
异步任务指的是,不进入主线程、而进入“任务队列”(task queue)的任务,只有等主线程任务执行完毕,”任务队列”开始通知主线程,请求执行任务,该任务才会进入主线程执行。
3.观察者模式Observer pattern(又称发布订阅模式publish–subscribe pattern)
JavaScript 并不是一行一行的分析并执行代码的,而是分段一段一段的进行分析并执行。
这里要说明一下,怎么才算一段。这里的段指的是 JavaScript 中的执行上下文。
执行上下文
函数执行上下文:这里指的是一个一个函数,一个函数执行前会先创建一个执行上下文
由此可见当JavaScript运行时会有很多执行上下文,为了方便管理,js引擎会创建一个执行上下文栈来对这些执行上下文进行管理,遵循先进后出原则。
全局上下文在栈的最底部,全局上下文只有一个在浏览器关闭时出栈。
函数在执行时会创建函数执行上下文,并放入执行栈中执行,执行完毕后出栈。
当 JavaScript 执行时,会将全局执行上下文放入执行栈中,接下来遇到函数执行上下文时会将这个上下文也放入执行栈中,执行完毕会出栈,当执行栈为空时,会从任务队列头部拿取一个任务,创建上下文并放入执行栈中执行。每当执行栈为空时总会循环的从任务队列获取任务,并创建执行上下文放入执行栈执行。这个循环我们称之为事件循环。
当 Node.js 启动时,它会初始化事件循环,处理提供的脚本,同步代码入栈直接执行,异步任务(网络请求、文件操作、定时器等)在调用 API 传递回调函数后会把操作转移到后台由系统内核处理。目前大多数内核都是多线程的,当其中一个操作完成时,内核通知 Node.js 将回调函数添加到轮询队列中等待时机执行。
首先事件循环进入定时器阶段,用于存储定时器的回调函数(setTimeout setInterval)。
执行与操作形同相关的回调函数。比如启动服务器应用时监听端口操作时的回调函数。
idle, prepare 阶段是给系统内部使用,idle 这个名字很迷惑,尽管叫空闲,但是在每次的事件循环中都会被调用,当它们处于活动状态时。
4.poll
存储I/O操作相关的回调函数,比如文件读写操作的回调函数。如果时间队列中有回调函数,执行他们直到清空操作。否则事件循环在此阶段会停留一段时间以等待新的回调含税进入。这个等待取决于两个条件:
(1).setImmediate队列(check阶段)存在要执行的回调函数。
(2).timers队列中存在要执行的回调函数,在这种情况下,事件循环将移至check阶段,然后移至Closing callbacks阶段,并最终从timers阶段进入下一次循环。
5.check
执行与关闭事件相关的回调函数。比如:关闭数据库连接的回调函数。
四、宏任务与微任务
任务队列的分类
任务队列分为两种,一种叫宏任务(macrotask),一种叫微任务(microtask )。
script( 整体代码)、setTimeout、setInterval、I/O(http请求)、UI 渲染。
Promise.then()、MutationObserver(监听dom的更改)。
两者的区别
执行原则
执行完一个宏任务回去检测微任务队列是否为空,如果不为空则执行完队列内的所有微任务,如果队列为空,则继续执行下一个宏任务,下一个宏任务执行完后会继续检测微任务队列是否为空,往复循环。
五、JS任务队列
我们知道,Javascript 这门脚本语言诞生的使命就是为处理页面中用户的交互,以及操作 DOM 而诞生的。
所以JS的设计就是单线程的,总不能多线程来操作DOM结构吧(那不就乱套了吗)。
那么什么是单线程,其实就是任务一个接着一个做,不能同时处理多个任务。
那这样就会导致一个问题,如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
console.log(1)
setTimeout(function () {
console.log(2);
process.nextTick(function () {
console.log(3);
})
})
Promise.resolve().then(function() {
console.log(4)
}).then(function() {
console.log(5)
})
反正肯定不是输出12345。
从上到下:
第一遍
setTimeout会放到宏任务队列
Promise会放到微任务队列
主线程先执行,然后微任务:
打印1,4,5
再执行宏任务:
主线程先执行,然后微任务
打印2,3
所以打印结果为:14523
OK,那么关于Js的任务队列也就解释清楚了
同步(Sync)发出一个功能调用时,必须一件一件事做,等前一件做完了才能做下一件事。
异步(Async)当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。
总结来说,同步和异步的区别:请求发出后,是否需要等待结果,才能继续执行其他操作。
原文地址:https://blog.csdn.net/weixin_55034688/article/details/129406333
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_35160.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!