>웹 프론트엔드 >JS 튜토리얼 >JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

不言
不言앞으로
2019-03-05 13:45:091866검색

이 기사는 JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예제)를 제공합니다. 필요한 참고 자료가 있으면 도움이 될 것입니다.

1. 소개

이 기사에서는 JavaScript 작동 메커니즘을 소개합니다. 이 부분은 비교적 추상적인 인터뷰 질문으로 시작하겠습니다.

console.log(1);
setTimeout(function(){
console.log(3);
},0);
console.log(2);
请问数字打印顺序是什么?
复制代码

이 질문은 매우 간단해 보이지만 JavaScript 작동 메커니즘을 이해하지 못한다면. , 그것은 매우 잘못된 대답입니다. 질문에 대한 답은 1 2 3 을 순서대로 출력하는 것입니다. 궁금한 점이 있으면 아래에 자세한 설명이 있습니다.

2. JS의 단일 스레딩 개념 이해

JavaScript 언어의 주요 기능은 단일 스레딩으로, 이는 동시에 한 가지 작업만 수행할 수 있음을 의미합니다. 그렇다면 JavaScript는 왜 다중 스레드를 가질 수 없습니까? 이렇게 하면 효율성이 향상될 수 있습니다. JavaScript는 목적에 따라 단일 스레드입니다. 브라우저 스크립팅 언어로서 JavaScript의 주요 목적은 사용자와 상호 작용하고 DOM을 조작하는 것입니다. 이는 단일 스레드만 가능하다는 것을 결정합니다. 그렇지 않으면 매우 복잡한 동기화 문제가 발생합니다. 예를 들어 JavaScript에 두 개의 스레드가 동시에 있다고 가정해 보겠습니다. 한 스레드는 특정 DOM 노드에 콘텐츠를 추가하고 다른 스레드는 해당 노드를 삭제합니다. 이 경우 브라우저는 어떤 스레드를 사용해야 합니까? 따라서 복잡성을 피하기 위해 JavaScript는 탄생부터 단일 스레드를 사용했으며 이는 언어의 핵심 기능이 되었으며 앞으로도 변하지 않을 것입니다.

3. 작업 대기열(메시지 대기열) 이해

단일 스레드는 모든 작업을 대기열에 넣어야 하며 다음 작업이 실행되기 전에 이전 작업이 완료된다는 의미입니다. 이전 작업이 오래 걸리면 다음 작업은 기다려야 합니다. JavaScript 언어의 디자이너들은 이 문제를 깨닫고 모든 작업을 두 가지 유형, 즉 동기 작업(동기)과 비동기 작업(비동기)으로 나누었습니다. 동기 작업은 메인 스레드에서 실행을 위해 대기 중인 작업을 의미합니다. 다음 작업은 이전 작업이 실행된 후에만 실행될 수 있습니다. "작업 대기열"이 비동기 작업이 실행될 수 있음을 메인 스레드에 알리면 작업이 실행을 위해 메인 스레드에 들어가게 됩니다. 비동기 작업에는 매크로 작업과 마이크로 작업이 포함됩니다(나중에 강조 표시됨). 다음으로 두 가지 예를 통해 동기 작업과 비동기 작업의 차이점을 설명하겠습니다.

console.log("A");
while(true){ }
console.log("B");
请问最后的输出结果是什么?
复制代码

답이 A라면 축하합니다. 정답을 맞췄습니다. 동기 작업이기 때문에 프로그램은 위에서 아래로 실행됩니다. while() 무한 루프가 발생하면 다음 명령문을 실행할 수 없습니다.

console.log("A");
setTimeout(function(){
console.log("B");
},0);
while(true){}
请问最后的输出结果是什么?
复制代码

답이 A라면 축하합니다. 이제 js에 대해 피상적으로 이해하게 되었습니다. 작동 메커니즘! 문제의 setTimeout()은 비동기 작업입니다. 모든 동기 작업이 실행될 때까지 비동기 작업은 실행되지 않습니다 이에 대해서는 아래에서 자세히 설명합니다.

4. 이벤트 루프 이해

비동기 실행의 작동 메커니즘은 다음과 같습니다.

  • 모든 동기 작업은 메인 스레드에서 실행되어 실행 컨텍스트 스택을 형성합니다.
  • 메인 스레드 외에 "작업 대기열"도 있습니다. 비동기 작업에 실행 결과가 있는 한 이벤트는 "작업 대기열"에 배치됩니다.
  • "실행 스택"의 모든 동기화 작업이 실행되면 시스템은 "작업 대기열"을 읽어 그 안에 어떤 이벤트가 있는지 확인합니다. 해당 비동기 작업은 대기 상태를 종료하고 실행 스택에 들어가 실행을 시작합니다.
  • 메인 스레드는 위의 세 번째 단계를 계속 반복합니다.

메인 스레드는 "작업 대기열"에서 이벤트를 읽습니다. 이 프로세스는 순환적이므로 전체 작동 메커니즘을 이벤트 루프라고도 합니다. 메인 스레드가 비어 있는 한 "작업 대기열"을 읽습니다. 이것이 JavaScript의 실행 메커니즘입니다. 이 프로세스는 계속해서 순환됩니다. 아래 그림은 이 점을 잘 보여줍니다.

JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

5. 어떤 문을 비동기 작업 대기열에 넣을지, 언제 넣을지

일반적으로 비동기 작업 대기열에 넣을 명령문은 다음 네 가지 유형이 있습니다.

  1. setTimeout和setlnterval
  2. DOM事件
  3. ES6中的Promise
  4. Ajax异步请求

javascript 代码运行分两个阶段

1、预解析---把所有的函数定义提前,所有的变量声明提前,变量的赋值不提前

2、执行---从上到下执行(按照js运行机制) 至于放入异步任务队列的时机,我们通过 setTimeout的例子和Ajax例子来详细说明:

例题1
for (var i = 0; i < 5; i++) {
setTimeout(function() {  
 console.log(i);  
  }, 1000);
}
请问最后的输出结果是什么?
复制代码

for循环一次碰到一个 setTimeout(),并不是马上把setTimeout()拿到异步队列中,而要等到一秒后,才将其放到任务队列里面,一旦"执行栈"中的所有同步任务执行完毕(即for循环结束,此时i已经为5),系统就会读取已经存放"任务队列"的setTimeout()(有五个),于是答案是输出5个5。

上面也提到,在到达指定时间时,定时器就会将相应回调函数插入“任务队列”尾部。这就是“定时器(timer)”功能关于定时器的重要补充

定时器包括setTimeout与 setInterval 两个方法。它们的第二个参数是指定其回调函数推迟/每隔多少毫秒数后执行。

对于第二个参数有以下需要注意的地方:

当第二个参数缺省时,默认为 0;

当指定的值小于 4 毫秒,则增加到 4ms(4ms 是 HTML5 标准指定的,对于 2010 年及之前的浏览器则是 10ms);也就是说至少需要4毫秒,该setTimeout()拿到任务队列中。

例题2
$.ajax({
url:&#39;xxxxx&#39;,
success:function (result){
console.log("a")
   }
})

setTimeout(function (){
console.log("b")
},100)

setTimeout(function (){
console.log("c")
})

console.log("d");

 JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

ajax加载完成时才会放入异步队列,至于这段时间不确定,所有有两种情况:①大于100ms,最后的结果是 d c b a ;②小于100ms,最后的结果便是d c a b。

六、微任务(Microtask)与宏任务(Macrotask)

我们上面提到异步任务分为宏任务和微任务,宏任务队列可以有多个,微任务队列只有一个

  • 宏任务包括:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • 微任务包括: new Promise().then(回调), process.nextTick, Object.observe(已废弃), MutationObserver(html5新特性)

当执行栈中的所有同步任务执行完毕时,是先执行宏任务还是微任务呢?

  • 由于执行代码入口都是全局任务 script,而全局任务属于宏任务,所以当栈为空,同步任务任务执行完毕时,会先执行微任务队列里的任务。
  • 微任务队列里的任务全部执行完毕后,会读取宏任务队列中拍最前的任务。
  • 执行宏任务的过程中,遇到微任务,依次加入微任务队列。
  • 栈空后,再次读取微任务队列里的任务,依次类推。

 JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

一句话概括上面的流程图:当某个宏任务队列的中的任务全部执行完以后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,就查看是否有其他宏任务队列

接下来我们看两道例子来介绍上面流程:

Promise.resolve().then(()=>{
  console.log(&#39;Promise1&#39;)  
  setTimeout(()=>{
    console.log(&#39;setTimeout2&#39;)
  },0)
})
setTimeout(()=>{
  console.log(&#39;setTimeout1&#39;)
  Promise.resolve().then(()=>{
    console.log(&#39;Promise2&#39;)    
  })
},0)
复制代码

最后输出结果是Promise1,setTimeout1,Promise2,setTimeout2

  • 一开始执行栈的同步任务执行完毕,会去查看是否有微任务队列,上题中存在(有且只有一个),然后执行微任务队列中的所有任务输出Promise1,同时会生成一个宏任务 setTimeout2
  • 然后去查看宏任务队列,宏任务 setTimeout1 在 setTimeout2 之前,先执行宏任务 setTimeout1,输出 setTimeout1
  • 在执行宏任务setTimeout1时会生成微任务Promise2 ,放入微任务队列中,接着先去清空微任务队列中的所有任务,输出 Promise2
  • 清空完微任务队列中的所有任务后,就又会去宏任务队列取一个,这回执行的是 setTimeout2
console.log(&#39;----------------- start -----------------&#39;);
setTimeout(() => {
  console.log(&#39;setTimeout&#39;);
}, 0)
new Promise((resolve, reject) =>{
  for (var i = 0; i < 5; i++) {
    console.log(i);
  }
  resolve();  // 修改promise实例对象的状态为成功的状态
}).then(() => {
  console.log(&#39;promise实例成功回调执行&#39;);
})
console.log(&#39;----------------- end -----------------&#39;);

 JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)

七、题外话

如果要输出0~4,上面例题应该如何修改?

  1. 将var变为let
for (let i = 0; i < 5; i++) {
setTimeout(function() {  
  console.log(i);
  }, 1000);
}
复制代码
  1. 加个立即执行函数
for (var i = 0; i < 5; i++) {
    (function(i){
    setTimeout(function() {  
      console.log(i);
      }, 1000);
    })(i)
}
复制代码
  1. 也可以通过这样加闭包
for(var i = 1;i < 5;i++){  
      var a = function(){  
          var j = i;    
        setTimeout(function(){  
              console.log(j);  
          },1000)  
      }    
    a();
}

위 내용은 JavaScript 작동 메커니즘에 대한 자세한 소개(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제