JavaScript 单线程相关

简介: 众所周知,Javascript是单线程执行的,这也就是说:JavaScript在同一个时间上只能处理一件事。他不像C,Java等这些多线程的,可以开不同的线程去同时处理多件事情。 那么为什么别的语言都可以这么方便的去开多个线程去同时执行多个任务,JavaScript却不行呢? “天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行指乱其所为,所以动心忍性,曾益其所不能”                                                 --《孟子》 正是因为JavaScript背负着重大的使命,所以他只能默默的看着别人拥有多线程。

众所周知,Javascript是单线程执行的,这也就是说:JavaScript在同一个时间上只能处理一件事。他不像C,Java等这些多线程的,可以开不同的线程去同时处理多件事情。

那么为什么别的语言都可以这么方便的去开多个线程去同时执行多个任务,JavaScript却不行呢?

“天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行指乱其所为,所以动心忍性,曾益其所不能”
                                                --《孟子》

正是因为JavaScript背负着重大的使命,所以他只能默默的看着别人拥有多线程。他作为浏览器脚本语言,只要是在于和用户进行交互/处理前端数据/操作DOM。

以代码举例吧:

  function addDom(){
    var html = document.createElement("div");
    html.innerHTML = "This is div "+i;
    document.getElementById("myDiv").appendChild(html);
  }
  function deleteDom(){
    var myDiv = document.getElementById("myDiv");
    //if(myDin.childNodes[0]){
        myDiv.removeChild(myDin.childNodes[0]);
    //}
  }

注意:以上代码注释部分可以让逻辑变的更严谨,此处需要报错已证明观点,所以加以注释。

按照以上代码执行addDom函数是能正确的生成这么一个DOM的吧,即便是在addDom函数还在执行过程中去执行deleteDom,deleteDom也将会在addDom执行完后才执行,那么就能完整的走完一个DOM的添加和删除操作了,这正是因为JavaScript是单线程。

现在我们假如JavaScript可以有多线程,那么我们让一个线程执行addDom函数的时候,在addDom还在执行的过程中再去执行deleteDom,这时候将会再开一个线程来执行,这时候浏览器脑子短路了,它不知道以哪个为主了...如果先执行完了deleteDom,那么效果就是即报了错还没达到想要的效果。

综上所述,JavaScript确实不适用于多线程,为了交互,他只能独自忍受了(所以当报错不会继续向下执行的时候,各位就别喷JavaScript了,好好检查自己写的代码才是这时候该做的)。

事件列队和异步执行

既然JavaScript是单线程执行的,那么有很多事件需要执行的时候,肯定需要排好队一个个来的吧。接下来我们就扯扯事件队列和异步。

JavaScript的事件队列里就是编排着接下来将要被逐个执行的事件,只有当前一个任务被执行完了,才会接下来执行后面一个任务。当我们触发一个事件,那么这个事件会被加入到事件列表末尾,当然不能插队咯,毕竟都是良民呐~

以上就是对事件队列的简单介绍,那么异步又是怎么回事呢?

Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步就是按照事件队列的顺序,有条不紊的执行下来。

异步则不同,每个任务都可以有一或多个回调函数(callback),当前面那一个任务执行完后,不去执行下一个任务,先执行其本身存在的回调函数,而下一个任务不等前一个任务结束,自顾自的开始执行了,这时候执行顺序就不一样的,产生异步了。(注意:这里并不说明使用回调函数即产生异步哦)

贴上代码:

  var num = 0;
  function firstFn(){
      num +=1;
  };
  function secondFn(value){
      if(value === 1){
          console.log("The num is 1"); //The num is 1
      } else{
          console.log("The num still 0");
      }
  }
  firstFn();
  secondFn(num);

以上是能正常打印出来的,因为先执行了第一个函数,所以这时num已经被加1了,所以判断生效。

  var num = 0;
  function firstFn(){
      setTimeout(function(){
         num +=1; 
      },0);  // 这里我们用setTimeout做了异步处理
  };
  function secondFn(value){
      if(value === 1){
          console.log("The num is 1");
      } else{
          console.log("The num still 0"); //The num still 0
      }
  }
  firstFn();
  secondFn(num);

这时候,再这样执行就没用了。因为setTimeout将num++的事件放到了事件列表的末尾去了,second(num)是在它的前面,所以现在执行是打印 The num still 0。那么怎么证明该事件被放到最后了呢?看下面的代码:

  <div id="myDiv" onclick="addEvent()">click me</div>
  var num = 0;
  function firstFn(){
      setTimeout(function(){
         num +=1; 
      },0);
  };
  function secondFn(value){
      if(value === 1){
          console.log("The num is 1"); //The num is 1
      } else{
          console.log("The num still 0");
      }
  }
  firstFn();
  function addEvent(){
      secondFn(num);
  } 

当我们点击id为myDiv的div的时候,将触发click事件吧,该事件会调用addEvent函数吧,addEvent函数会在事件队列的末尾加入新的需要执行的事件吧。这时候我们点击该div,就会打印 The num is 1 了。

同理理解setInterval。

下面顺便贴一段有小伙伴提问过的问题代码:

正常的代码:

  var i = 3;
  for(;i>0;i--){
    console.log(i);  //打印顺序:3   2   1
  };

"不正常"的代码:

  var i = 3;
  for(;i>0;i--){
    setTimeout(function(){console.log(i);},0);  //打印顺序 0   0   0
  };

人家问的就是为什么都设置延迟时间为0了,打印出来的还是不正常的。这也是因为异步,当函数被执行的时候,i的值已经为0。

对于setTimeout的通常描述:给定一个回调及N毫秒的延迟,setTimeout将会在N毫秒后运行该回调。

所以大多数情况下,这个描述只能算大致正确,不能算完全正确。

而且setTimeout还附带了个隐藏的可大可小的坑(将由线程阻塞导致):

  var start = new Date;
  setTimeout(function(){
      var end = new Date;
      console.log("End Time: ",end - start," ms"); // 有几次打印的是End Time:  1001  ms ,这还是在没有其他阻塞的,只运行这一个事件的情况下出现偏差
  },1000);

所以上面那个描述的N毫秒将在某些情况下会出现偏差。
好了,就写这么多先吧。明天还得上班呢,今天算拖的很晚了...还是手机编辑的,本来打算睡觉,写一半没完成,心里怪难受的...
如理解有偏差,还望大家不吝指教,大家一起交流才能更好的进步。

相关文章
|
数据采集 并行计算 JavaScript
实战指南:在 Node.js 中利用多线程提升性能
在 Node.js 的世界中,多线程技术一直是一个受到广泛关注的领域。最初,Node.js 设计为单线程模式。随着技术发展,Node.js 引入了多线程支持,进而利用多核处理器的强大性能,提升了应用性能。接下来的内容将深入探讨 Node.js 如何实现多线程,以及在何种场合应该采用这种技术。
|
Web App开发 JavaScript 前端开发
[译] 深入理解 Node.js 中的 Worker 线程
[译] 深入理解 Node.js 中的 Worker 线程
|
前端开发 JavaScript
JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。
【6月更文挑战第22天】JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。回调是基础,用于在操作完成后执行函数;Promise管理异步状态,支持链式调用;async/await提供同步代码外观,简化错误处理。每种技术在处理耗时任务时都起着关键作用。
190 3
|
JavaScript 前端开发
JS 单线程还是多线程,如何显示异步操作
JS 单线程还是多线程,如何显示异步操作
149 2
|
消息中间件 前端开发 JavaScript
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
|
前端开发 JavaScript UED
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
|
前端开发 JavaScript
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
219 1
|
消息中间件 JavaScript 前端开发
Node.js 中的线程 与 并发
Node.js 中的线程 与 并发
178 0
|
前端开发 JavaScript API
🍉如何理解单线程的JavaScript及其工作原理|8月更文挑战
🍉如何理解单线程的JavaScript及其工作原理|8月更文挑战
330 7
🍉如何理解单线程的JavaScript及其工作原理|8月更文挑战
|
自然语言处理 JavaScript 前端开发
WebWorker——JavaScript也可以“多线程”
虽然JavaScript是单线程的,但是浏览器是多线程的啊,我们可以借助浏览器再跑一个JavaScript线程来帮助主JavaScript线程减轻压力,JavaScript之所以是单线程的原因是要保持数据的一致性,防止多个线程修改数据导致数据不一致,所以虽然可以使用worker,但也要遵循一些规范
491 0
WebWorker——JavaScript也可以“多线程”

热门文章

最新文章