浅谈javaScript的运行机制

1,357 阅读4分钟

1.关于javaScript

在我们所学习的javaScript知识中,javaScript是单线程语言,同时只能执行一项任务。其实javaScript中的多线程是用多线程模拟出来的。

2.关于进程、单线程、多线程

- 进程
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。
- 单线程
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
- 多线程 
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

3.javaScript中的同步和异步

单线程就意味着,所有的任务都需要排队,只有前一个任务执行结束了,才会执行后一个任务。如果前一个任务没有执行结束或遇到一个耗时很长的任务,那么后一个任务就处于一直等待的状态。
于是javaScript语言的设计者意识到了这个问题,于是js将任务分为两种:同步任务与异步任务。
- 同步任务
同步任务就是后一个任务必须等待前一个任务执行完才能执行,是在主线程上执行
- 异步任务
异步任务就是不会直接进去进入主线程执行,而是进入任务队列,只有当任务队列通知异步任务可以执行时,异步任务才会被推入主线程执行。

4.setTimeout和Promise

  • 事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
  • 在js中,异步任务除了有setTimeout这类的异步任务,还有一类就是es6中很常用promise...then这类的异步任务,因此除了同步任务和异步任务,任务还可以更加细分为macrotask(宏任务)microtask(微任务)
  • macrotask: 包括setTimeout、setInterval和执行栈
  • microtask: 包括Promise、process.nextTick
	  // Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
        console.log('enent-loop');//1
        // setTimeout()=>{}挂起
        // setTimeout1
        setTimeout(()=>{//event-loop event-task 任务队列 -> 属于主线程 队列结构 当事件信息
            // 当主线程停下来后 轮询 enent-loop task-queue
            console.log('setTimeout1');//2
            Promise.resolve().then(data=>{//promise
                console.log(2222);//3
            })
        },0)
        //setTimeout2
        setTimeout(()=>{//宏任务  
            console.log('setTimeout2');//4
        },0)
        Promise.resolve().then(data=>{//promise-> 微任务
            console.log(1111);//5
        })
        console.log('enent-loop end');//6
        // 运行顺序为1 6 5 2 3 4 每次只做一个任务

分析上面执行过程:

  • 第一轮:主线程开始执行,先执行 console.log(),输出enent-loop
  • 遇到setTimeout记为setTimeout1,将setTimeout的回调函数丢到宏任务队列中。
  • 再往下执行遇到setTimeout记为setTimeout2,将setTimeout的回调函数丢到宏任务队列中。
  • 再往下继续执行遇到Promise.resolve().then的回调函数丢到微任务队列中。
  • 再继续执行console.log(),输出enent-loop end
  • 再继续执行 Promise.resolve().then,输出1111
  • 第二轮: 从宏任务队列,发现setTimeout1回调,执行console.log(),输出setTimeout1
  • 再遇到Promise.resolve().then,输出2222
  • 再往下执行setTimeout2,输出setTimeout2
    最后的输出顺序为:
    enent-loop
    enent-loop end
    1111
    setTimeout1
    2222
    setTimeout2

setTimeout

我们知道setTimeout是用于异步延时执行的。下面例子是实现延时2秒执行:

setTimeout(() => {
   console.log('延时2秒');
   },2000)

console.log

console.log我们经常使用到,但是不知道console.log是同步任务吧!

console.log('同步任务!');

Promise

Promise对象代表了未来将要发生的事件,用来传递异步操作的消息。

let promise = new Promise(function(resolve, reject){
   console.log("AAA");
   resolve()
});
Promise.resolve().then(data=>{
           console.log('BBB');
       })
    console.log("CCC")

执行后,我们发现输出顺序总是 AAA -> CCC -> BBB。在Promise新建后会立即执行,所以首先输出 AAA。然后,then方法指定的回调函数将在当前脚本所有同步任务执行完后才会执行,所以再执行CCC 再执行then输出BBB