对浏览器端javaScript运行机制的理解

浏览器端javaScript运行机制的理解

  1. 线程
  2. 同步异步
  3. Event-Loop

线程

Javascript语言的执行环境是"单线程"(single thread)。
所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。而Javascript运行在浏览器上,然而浏览器内核是多线程的,它包含以下几个线程:

  • GUI 渲染线程
  • JavaScript引擎线程
  • 定时触发器线程
  • 事件触发线程
  • 异步http请求线程

我们平时使用到最多的就是GUI 渲染线程,JavaScript引擎线程,异步http请求线程。

同步异步

浏览器端能够异步主要还是因为Event-Loop这个默默的在幕后执行。

1.同步

同步,也就是JavaScript在执行的时候是按照顺序执行,前一个任务执行完毕之后才会执行下一个任务。因此当遇到比较耗时的任务时,就是使得后边的任务长时间的等待,例如当浏览器端执行一段耗时js代码,就会使得浏览器假死。因此对于这种情况就需要将耗时的任务异步执行,不阻塞其他非耗时任务的执行。

2.异步

异步,简单说就是,将耗时任务放入其他线程执行,主线成会在将来的某个时刻收到回调通知。
浏览器端的异步方式:
- 回调函数
这是异步编程最基本的方法。

假定有两个函数f1和f2,后者等待前者的执行结果。

  f1();

  f2();

如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。

function f1(callback){

    setTimeout(function () {

      // f1的任务代码

      callback();

    }, 1000);

  }

执行代码就变成下面这样:

  f1(f2);

采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

- 事件监听

另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

还是以f1和f2为例。首先,为f1绑定一个事件(这里采用的jQuery的写法)。

  f1.on('done', f2);

上面这行代码的意思是,当f1发生done事件,就执行f2。然后,对f1进行改写:

  function f1(){

    setTimeout(function () {

      // f1的任务代码

      f1.trigger('done');

    }, 1000);

  }

f1.trigger('done')表示,执行完成后,立即触发done事件,从而开始执行f2。

这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰

- Promises对象
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。

简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

function Client() {
}

Client.prototype.cancel = function (options) {
  options.cancelFlag = true;
};

Client.prototype.operation = function (options) {
  var options = options || {};
  options.cancelFlag = false;
  return new Promise(function (resolve, reject) {
    //异步请求开始
    setTimeout(function () {
      if(!options.cancelFlag){
        resolve('task finish');
      } else {
        reject(new Error('cancel'));
      }
    }, 1000);
  });
};

var client1 = new Client();
var multipartOptions = {
  cancelFlag: false
}
client1.operation(multipartOptions).then(function (value) {
  console.log(value);
}).catch(function (err) {
  console.log(err);
});
client1.cancel(multipartOptions);
console.log('cancel');

但是Promise一旦执行,不可取消。

Event-Loop

一张图来看一下
对浏览器端javaScript运行机制的理解

JavaScript引擎的内部运行机制跟Event loop没有任何的关系。
event loops隐藏得比较深,很多人对它很陌生。但提起异步,相信每个人都知道。异步背后的“靠山”就是event loops。这里的异步准确的说应该叫浏览器的event loops或者说是javaScript运行环境的event loops,因为ECMAScript中没有event loops,event loops是在HTML Standard定义的。
总结一点就是,除了js引擎线程外的其他线程在执行完任务后都会将结果加入到Event-Loop的任务队列中,只有当js引擎线程将当前任务执行完后才会去继续执行Event-Loop的任务队列的任务。

相关参考
https://www.jianshu.com/p/1ee6c21f6efa
https://juejin.im/entry/59760fc15188250d8d193279
http://imweb.io/topic/58e3bfa845e5c13468f567d5
http://es6.ruanyifeng.com/?search=yield&x=0&y=0#docs/promise
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html

上一篇:针对租户的用户分层和权益设计(相册与网盘服务)


下一篇:Win7系统分区(C盘)扩容的一种可行的解决方案