JavaScript 同步异步详解

276 阅读2分钟

JS执行机制

    在讲解同步异步之前先来了解一下js的执行机制吧,JavaScript是一门单线程的、非阻塞的脚本语言。单线程意味着,JS 代码在执行的任何时候,都只有一个主线程来处理所有的任务,而js代码一旦报错,后面的代码将不再执行,这也就意味着js无法进行多线程编程,但是js中存在着无处不在的异步概念,我们如何理解呢?理解异步和非阻塞靠的就是 Event Loop(事件循环);

event loop

EventLoop 轮询处理线程:
我们可以把它理解为一个中介,在主线程、异步线程与消息队列三者之间进行交流与沟通。如下图所示:从主线程那里顺时针的看,整个的流程是循环往复的。只有当主线程的同步代码都执行完了,才会去队列里看看还有什么要执行的。

image.png

同步与异步:

  • 同步任务: 立即执行的任务队列,比如一个简单的函数;
  • 异步任务: 请求接口发送 ajax,发送 promise,或时间计时器等等;

异步任务又分为:微任务与宏任务
微任务:promise,async/aswait
宏任务:setTimeout,setInterval,Ajax,Dom事件
在任务队列中微任务比宏任务优先执行;下面的代码中我们来看一下微任务宏任务与dom渲染的执行顺序

const p1 = document.createElement('p');
        p1.innerHTML = '一段文字';
        // const p2 = `<p>一段文字</p>`;
        // const p3 = `<p>一段文字</p>`;

        document.querySelector('#container').appendChild(p1);


        // 微任务 dom渲染前触发
        Promise.resolve().then(() => {
            console.log('微任务lenght1', document.querySelector('#container').children.length);
            alert('promise then') //Dom渲染了吗  -没有
        })


        // 宏任务 dom渲染后触发
        setTimeout(() => {
            console.log('宏任务length2', document.querySelector('#container').children.length);
            alert('setTimeout') //Dom渲染了吗 -渲染了
        })
        
        
            /*
           顺序:
           微任务lenght1 1
           alert('promise then')
           <p></p>
           宏任务length2 1
           alert('setTimeout')
        */

代码运行后可以得出以下两点:微任务 dom渲染前触发,宏任务 dom渲染后触发;下面的图带大家看微任务宏任务在event loop中的执行,再执行代码时,微任务会放在 mico task queue中而不是web APIS中

image.png

下面是我对于同步异步执行顺序的总结:

  1. 首先走同步,call stack清空
  2. call stack清空后执行微任务
  3. 尝试dom渲染
  4. 触发event loop执行宏任务