JS运行机制&Promise

290 阅读4分钟

单线程的概念

JS是一门单线程语言。因此,JS在同一个时间只能做一件事。单线程意味着,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务。

为什么是单线程?

JS的单线程,与它的用途是有很大关系。我们都知道,JS作为浏览器的脚本语言,主要用来实现与用户的交互,利用JS,我们可以实现对DOM的各种各样的操作,如果JS是多线程的话,一个线程在一个DOM节点中增加内容,另一个线程要删除这个DOM节点,那么这个DOM节点究竟是要增加内容还是删除呢?这会带来很复杂的同步问题,因此,JS是单线程的。

任务队列

因为JS的单线程,所以同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务。但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着。拿ajax来说,当用户像后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验。

因此,JS在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载完成,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务。因此,任务就可以分为同步任务异步任务

任务队列其实是一个先进先出的数据结构

JS的所有任务分类:同步任务和异步任务

主线程和任务队列的示意图

同步任务和异步任务

异步任务

异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。

举例Demo-1:

setTimeout(()=>{
    console.log('setTimeout')
},0)

new Promise(resolve=>{
    resolve()   //无论resolve()放在上面还是下面,console.log('promise')都是同步优先执行
    console.log('promise')
})
.then(value=>console.log('成功'))

console.log('执行顺序')

打印结果依次为:promise、执行顺序、成功、setTimeout

但是一定是向上面所说的执行吗?看下面的Demo-2

let promise=new Promise(resolve=>{
    setTimeout(()=>{ 
        //setTimeout这个宏任务不执行,那么里面的resolve()这个微任务就没办法创建
        // 虽然优先级是:同步任务>微任务>宏任务
        // 但是这个例子里面,宏任务优先于微任务执行
        resolve()
        console.log('setTimeout')
    },0)
    console.log('promise')
}).then(value=>console.log('成功'))

console.log('哈哈哈')

虽然优先级是:同步任务>微任务>宏任务,但是并不一定就是宏任务最后执行,具体情况具体分析

总的来说,JS的异步机制包含以下几个步骤:

1. 所有同步任务都在主线程上执行,形成一个执行栈

2. 主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件

3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行

4. 主线程不断地重复上面的第3步

以下举例:看执行顺序:

    async function async1(){
        console.log('async1 start')
        await async2()
        console.log('async1 end')
    }

    async function async2(){
        console.log('async2')
    }

    console.log('script start')

    setTimeout(function(){
        console.log('setTimeout') 
    },0)

    async1();

    new Promise(function(resolve){
        console.log('promise1')
        resolve();
    }).then(function(){
        console.log('promise2')
    })

    console.log('script end')

1. 从上到下,先走同步任务队列,再走异步任务队列

2. 同步任务:script start → async1 start → async2 → promise1 → script end

3. 微观任务:async1 end → promise2

4. 宏观任务:setTimeout

对Promise的理解

1. Promise是一个立即执行的同步函数

2. 当执行到.then .catch .finally .all .race等时,是异步的

3. Promise的首字母大写,Promise是一个构造函数,可以new Promise()得到一个Promise的实例

4. 每个Promise都有返回结果,返回结果就是Promise

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
})
//同步执行


.then(function() {
    console.log('promise2');
});
//异步

async await

1. await这一行以及它上面的都是同步执行(绿框同步立即执行)

2. await之后是异步的微任务(红线画出来的,放在微任务队列中稍后执行)