学习一门知识,有些内容必须要提前明白,比如在学习js中同步异步的问题前,需要明白,js是单线程的,为什么它得是单线程的呢?现在先从它应用的场景来说,就是用来让用户与页面进行交互的吧。假如有js是多线程的,那在这个线程里面,用户点击某个按钮会增加一个DOM节点,在另一个线程里面,用户点击这个按钮又会删除一个DOM节点,那么此时js就不知道该听谁的了。这就是为什么会出现同步异步。假设没有异步,那么我们在向服务器请求数据时,可能会因为网络不好,卡了半天,这时候没有用到同步异步,让网页在等待数据请求回来不可以继续和用户交互,这样会导致整个网页很奇怪的卡住了,用户体验很不好。
执行栈与任务队列
执行栈
简单来说就是先放先出,后放后出。
那么执行栈就是把图中的内容块变成代码任务,光说肯定说不明白,还是得上代码:
function fn (count) { if (count <= 0) return fn(count - 1) console.log(count) } fn(3)
这是一段很简单的递归代码,这里我们直接上图解释(这里其实画的不严谨,栈底应该是全局执行上下文):
js中所有的任务都会在主线程上执行然后形成一个执行栈。(请记住这一点!!!)
任务队列
其实队列和栈则是相反的,队列是先进先出的。在任务队列通俗理解就是用来放置异步任务的回调函数的。(也请记住这一点!!!)
同步任务与异步任务
先上一点概念性的东西,打个基础:
同步任务
先说清楚同步任务不是同步一起执行的。言简意赅来说就是在等待上一个执行任务结束才可以执行下一个任务,我们可以看看下面写段简单的代码解释一下:
console.log(1) console.log(2) console.log(3)
代码很简单吧,很明显输出结果是1,2,3,这就是同步代码,那么我们就可以总结了,同步任务就是在主线程上面排队,然后一个一个进入执行栈执行,直到执行栈为空。
异步任务
还是直接举个栗子:
console.log(1) setTimeout(() => { console.log(2) }, 1000) console.log(3)
这段代码的输出和上面同步代码的输出不一样,它的输出顺序是1,3,2,这就是异步代码,它并不会按照执行顺序去执行,
总结下异步任务:异步任务指的是,不进入主线程、而进入"任务队列"(Event queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
js的执行机制
先上比较晦涩难懂的概念:
1.同步任务由JavaScript 主线程按顺序执行。
2.异步任务委托给宿主环境执行。
3.异步任务完成后,对应的回调函数会被加入到任务队列中等待执行,任务队列又被分为宏任务队列和微任务队列,优先执行微任务队列,常见的微任务有new Promise().then,常见的宏任务有定时器
4.JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行。
5.JavaScript 主线程不断重复上面的第4 步,在执行回调函数时又会按照上面的四步去执行。
js一直从任务队列中取回调函数,然后放入主线程中执行,这是一个循环不断的过程,所以把它叫做事件循环。
这个还是要简单粗暴的来段代码会更直观一点:
const promise = new Promise((resolve, reject) => { console.log(1); setTimeout(() => { console.log("timerStart"); resolve("success"); console.log("timerEnd"); }, 0); console.log(2); }); promise.then((res) => { console.log(res); }); console.log(4);
其实代码不懂不重要,只要你就把自己当成js代码的检察官,要正确把它们放在合适的“位置”
检察官的第一步就是明确是步代码和异步代码,我们从上往下看,Promise本身是同步的,所以它应该在主线程上排队,然后继续看pomise.then是个异步任务,并且是属于微任务的,它的回调函数应该在微任务队列中(此时还不在),最后一句输出语句是同步代码,应该在主线程上排队。
第二步,执行主线程上的同步代码,首先有Promise排着队呢,所以先输出1,随后有个定时器,所以应该把它挂起执行,由于它没有时间延迟,所以回调函数直接被放入宏任务队列,继续执行代码,遇到打印,直接输出2。现在主线程还有其他的同步代码不?是不是还有一个输出语句,所以输出4,现在主线程上的同步代码执行完了
第三步读取任务队列,我们要知道在微任务队列上没有东西(Promise的状态无变动,就不会执行promise.then()),所以读取宏任务队列上的回调函数,回调函数进入主线程执行,首先输出timerStart,然后promise状态发生改变,然后又遇到一个输出语句,输出timerEnd。现在主线程上又没有东西了,又得去看任务队列上有没有东西了。
第四步,其实当promise的状态有了改变,这样在微任务队列上有回调函数了,执行输出语句,res为success,输出success
关于JavaScript事件循环同步任务与异步任务已讲述完了。学习才是最好的进步,欢迎大家多多关注后续更多精彩知识。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/128175.html
摘要:的单线程,与它的用途有关。特点的显著特点异步机制事件驱动。队列的读取轮询线程,事件的消费者,的主角。它将不同的任务分配给不同的线程,形成一个事件循环,以异步的方式将任务的执行结果返回给引擎。 这两天跟同事同事讨论遇到的一个问题,js中的event loop,引出了chrome与node中运行具有setTimeout和Promise的程序时候执行结果不一样的问题,从而引出了Nodejs的...
摘要:主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环。上面也提到,在到达指定时间时,定时器就会将相应回调函数插入任务队列尾部。这就是定时器功能。关于定时器的重要补充定时器包括与两个方法。 一、引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: console.log(1); setTimeout(function()...
摘要:主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环。上面也提到,在到达指定时间时,定时器就会将相应回调函数插入任务队列尾部。这就是定时器功能。关于定时器的重要补充定时器包括与两个方法。 一、引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: console.log(1); setTimeout(function()...
摘要:主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环。上面也提到,在到达指定时间时,定时器就会将相应回调函数插入任务队列尾部。这就是定时器功能。关于定时器的重要补充定时器包括与两个方法。 一、引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: console.log(1); setTimeout(function()...
阅读 495·2023-03-27 18:33
阅读 701·2023-03-26 17:27
阅读 602·2023-03-26 17:14
阅读 571·2023-03-17 21:13
阅读 489·2023-03-17 08:28
阅读 1747·2023-02-27 22:32
阅读 1254·2023-02-27 22:27
阅读 2056·2023-01-20 08:28