likes
comments
collection
share

JavaScript运行机制

作者站长头像
站长
· 阅读数 0

Event Loop机制

JavaScript运行机制

JavaScript运行机制 js引擎遇到一个异步任务后并不会一直等待其返回结果,而是会将这个任务交给浏览器的其他模块进行处理(以webkit为例,是webcore模块),继续执行调用栈中的其他任务。当一个异步任务返回结果后,js引擎会将这个任务加入与当前调用栈不同的另一个队列,我们称之为事件队列。

任务队列和事件循环

JavaScript运行机制

JavaScript运行机制 JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)

首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。

异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。

JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。

宏观任务(macro-task)、微观任务(micro-task)

JavaScript运行机制

宏任务(macro-task): 整体代码script、setTimeout、setInterval… 微任务(micro-task):Promises、Object.observe…

宏观任务:宿主发起的任务为宏观任务,如setTimeout、setInterval、setImmediate,I/O 微观任务:JavaScript引擎发起的任务为微观任务,如Promise

JavaScript引擎等待宿主环境分配宏观任务,宏观任务的队列可以理解为一个事件循环

异步操作

同步任务(synchronous)和异步任务(asynchronous)

程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。

同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应。

举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。

异步操作模式

回调函数

function f1(callback) {
  // ...
  callback();
}

function f2() {
  // ...
}

f1(f2);

事件监听


function f1() {
  setTimeout(function () {
    // ...
    f1.trigger('done');
  }, 1000);
}

f1.on('done', f2);

发布/订阅


function f1() {
  setTimeout(function () {
    // ...
    jQuery.publish('done');
  }, 1000);
}

jQuery.unsubscribe('done', f2);

Promise

   function move(ele, target, dir) {
        return new Promise(resolve => {
            function fn() {
                let startLeft = parseInt(getComputedStyle(ele, null)[dir]);
                // console.log(startLeft)
                // let speed =(target-startLeft)/ Math.abs( target-startLeft ) ;
                let speed = (target - startLeft) > 0 ? 1 : -1;
                setTimeout(() => {
                    startLeft += speed;
                    if (startLeft === target) {
                        // console.log("运动完成");
                        // cb && cb();
                        resolve("运动完成");
                    } else {
                        ele.style[dir] = startLeft + "px";
                        fn();
                    }
                }, 10);
            }
            fn();
        })
    }

Async、await

void async function asyncmove(){
    await move(ele, 300, "left");
    await move(ele, 300, "top");
    await move(ele, 0, "left");
    await move(ele, 0, "top");
}()

Generator、迭代器

function*fn(){
    yield new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log("a");
            resolve(1);
        },500);
    });
    yield new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log("b");
            resolve(2);
        },500);
    });
    yield new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log("c");
            resolve(3);
        },500);
    });
}
co(fn);
function co(fn){
    let f = fn();
    next();
    function next(data){
        let result = f.next();
        if(!result.done){
            // 上一个异步走完了,再执行下一个异步
            result.value.then((info)=>{
                console.log(info,data);
                next(info);
            });
        }
    }
}

参考资料

Event Loop JavaScript 运行机制详解:再谈Event Loop 并发模型与事件循环 异步操作 《Tasks, microtasks, queues and schedules》 JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式! 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 JavaScript事件循环机制 JavaScript中的Event Loop(事件循环)机制 微任务、宏任务、同步、异步、Promise、Async、await xxxx