この記事の目的は、JavaScript の実行メカニズムを徹底的に理解することです。この記事を読んでもまだ理解できない場合は、私を倒しても構いません。
あなたが JavaScript の初心者であろうとベテランであろうと、仕事の面接中であろうと日々の開発作業を行っていようと、私たちはよく次のような状況に遭遇します: 数行のコードが与えられ、その出力を知る必要がある内容も順番も。 JavaScript はシングルスレッド言語であるため、次のような結論を導き出すことができます:
読者は、これを見たときに打ちのめされるでしょう。このラオ: js は 1 行ずつ実行されることを知りませんか?それを言う必要さえありますか? js は 1 行ずつ実行されるので、お待ちください。したがって、js は次のようになると考えられます:
let a = '1';console.log(a);let b = '2';console.log(b);复制代码
しかし、実際には、js は次のようになります。
setTimeout(function(){ console.log('定时器开始啦') });new Promise(function(resolve){ console.log('马上执行for循环啦'); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log('执行then函数啦') });console.log('代码执行结束');复制代码
js はステートメントの出現順に実行されるという概念に従って、自信を持って出力を書き留めました。 results :
//"定时器开始啦"//"马上执行for循环啦"//"执行then函数啦"//"代码执行结束"复制代码
1. javascript について
言語です。Web-Worker は最新の HTML5 で提案されていますが、シングルスレッドである JavaScript の核心はまだ変わらない。したがって、「マルチスレッド」のすべての JavaScript バージョンは単一のスレッドでシミュレートされ、すべての JavaScript マルチスレッドは張り子の虎です。2.javascript イベント ループ
これまで多くのことを述べてきましたが、次のコードを記述する方が簡単です:
let data = []; $.ajax({ url:www.javascript.com, data:data, success:() => { console.log('发送成功!'); } })console.log('代码执行结束');复制代码
上記は単純な
ajaxリクエスト コードです:
実行
ajax イベントが完了すると、コールバック関数
メインスレッドは、コールバック関数
3. 好き嫌いが分かれる setTimeout
言うまでもなく、非同期実行は遅延する可能性があるということは誰もが抱く第一印象です。 , 3 秒の遅延が達成されます:
徐々にsetTimeout(() => { console.log('延时3秒'); },3000)复制代码
がより多くの場所で使用され、問題が発生します。遅延が 3 秒と明確に記述されている場合もありますが、実際の遅延は5、6秒。関数の実行には数秒かかりますが、何が起こっているのでしょうか? 先看一个例子: 根据前面我们的结论, 去验证一下,结果正确! 乍一看其实差不多嘛,但我们把这段代码在chrome执行一下,却发现控制台执行 这时候我们需要重新理解 上述的流程走完,我们知道 我们还经常遇到 答案是不会的, 代码1的输出结果是: 代码2的输出结果是: 关于 上面说完了 唯一需要注意的一点是,对于 传统的定时器我们已经研究过了,接着我们探究 我们进入正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义: 不同类型的任务会进入对应的Event Queue,比如 事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。听起来有点绕,我们用文章最开始的一段代码说明: 事件循环,宏任务,微任务的关系如图所示: 我们来分析一段较复杂的代码,看看你是否真的掌握了js的执行机制: 第一轮事件循环流程分析如下: 上表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1和7。 我们发现了 好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。那么第二轮时间循环从 コード全体が合計 3 つのイベント ループで実行され、完全な出力は 1、7、6、8、2、4、3、5、9、11、10、12 になります。 最初から JavaScript はシングルスレッド言語であると述べてきましたが、どのような新しいフレームワークや新しい構文シュガーがいわゆる非同期を実装していても、実際には同期メソッドを使用してシミュレートされます。しっかりと把握してください。シングル スレッドは非常に重要です。 イベントループとは、js が非同期を実現するためのメソッドであり、js の実行機構でもあります。 実行と実行には大きな違いがあり、JavaScript はノード、ブラウザ、Ringo などの環境ごとに異なる方法で実行されます。 。この操作は主に、統合された JavaScript 解析エンジンを参照します。 マイクロタスクとマクロタスクには、 以上がJavaScriptの実行メカニズムを徹底的に理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。setTimeout(() => { task(); },3000)console.log('执行console');复制代码
setTimeout
是异步的,应该先执行console.log
这个同步任务,所以我们的结论是://执行console//task()复制代码
然后我们修改一下前面的代码:setTimeout(() => { task() },3000) sleep(10000000)复制代码
task()
需要的时间远远超过3秒,说好的延时三秒,为啥现在需要这么长时间啊?setTimeout
的定义。我们先说上述代码是怎么执行的:
task()
进入Event Table并注册,计时开始。sleep
函数,很慢,非常慢,计时仍在继续。timeout
完成,task()
进入Event Queue,但是sleep
也太慢了吧,还没执行完,只好等着。sleep
终于执行完了,task()
终于从Event Queue进入了主线程执行。setTimeout
这个函数,是经过指定时间后,把要执行的任务(本例中为task()
)加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于3秒。setTimeout(fn,0)
这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?setTimeout(fn,0)
的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。举例说明://代码1console.log('先执行这里'); setTimeout(() => { console.log('执行啦') },0);复制代码
//代码2console.log('先执行这里'); setTimeout(() => { console.log('执行啦') },3000);复制代码
//先执行这里//执行啦复制代码
//先执行这里// ... 3s later// 执行啦复制代码
setTimeout
要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。有兴趣的同学可以自行了解。4.又恨又爱的setInterval
setTimeout
,当然不能错过它的孪生兄弟setInterval
。他俩差不多,只不过后者是循环的执行。对于执行顺序来说,setInterval
会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。setInterval(fn,ms)
来说,我们已经知道不是每过ms
秒会执行一次fn
,而是每过ms
秒,会有fn
进入Event Queue。一旦setInterval
的回调函数fn
执行时间超过了延迟时间ms
,那么就完全看不出来有时间间隔了。这句话请读者仔细品味。5.Promise与process.nextTick(callback)
Promise
与process.nextTick(callback)
的表现。Promise
的定义和功能本文不再赘述,不了解的读者可以学习一下阮一峰老师的Promise。而process.nextTick(callback)
类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。
setTimeout
和setInterval
会进入相同的Event Queue。setTimeout(function() { console.log('setTimeout'); })new Promise(function(resolve) { console.log('promise'); }).then(function() { console.log('then'); })console.log('console');复制代码
setTimeout
,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)Promise
,new Promise
立即执行,then
函数分发到微任务Event Queue。console.log()
,立即执行。then
在微任务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') }) })复制代码
console.log
,输出1。setTimeout
,其回调函数被分发到宏任务Event Queue中。我们暂且记为setTimeout1
。process.nextTick()
,其回调函数被分发到微任务Event Queue中。我们记为process1
。Promise
,new Promise
直接执行,输出7。then
被分发到微任务Event Queue中。我们记为then1
。setTimeout
,其回调函数被分发到宏任务Event Queue中,我们记为setTimeout2
。
宏任务Event Queue
微任务Event Queue
setTimeout1
process1
setTimeout2
then1
process1
和then1
两个微任务。process1
,输出6。then1
,输出8。setTimeout1
宏任务开始:
process.nextTick()
,同样将其分发到微任务Event Queue中,记为process2
。new Promise
立即执行输出4,then
也分发到微任务Event Queue中,记为then2
。
宏任务Event Queue
微任务Event Queue
setTimeout2
process2
then2
process2
和then2
两个微任务可以执行。process.nextTick()
分发到微任务Event Queue中。记为process3
。new Promise
,输出11。then
分发到微任务Event Queue中,记为then3
。
宏任务Event Queue
微任务Event Queue
process3
then3
process3
とthen3
が実行されます。
(ノード環境でのイベント監視はlibuvに依存しており、フロントエンド環境は完全に同じではないため、出力順序に誤差が生じる場合がありますのでご注意ください)6.最後に記述
(1)js 非同期
(2) イベントループ イベントループ
(3)JavaScript の実行と実行
(4)setImmediate
setImmediate
など、さまざまな種類があります。実行方法はすべて共通です。興味のある学生は自分で実行できます。学ぶ。(5) 最後に
# # 2つの基本ポイントをしっかり押さえて、JavaScriptを本気で勉強して、フロントエンドマスターになるという大きな夢を一日も早く実現しましょう!