Skip to content

Asap原理分析 #68

Description

@xiaoxiaosaohuo

asap 原理分析

什么是asap?

asap是as soon as possible的缩写,在Node和浏览器环境下能将回调函数以高优先级任务来执行(下一个事件循环之前)。Promise/A+的异步任务实现就是基于asap这个库。

ASAP会努力的在IO,重排,重绘等事件之前调度任务,也就是常说的会把任务放进微任务队列执行。

usage

asap(function () {
    // ...
});

实现

定义全局变量

// 任务队列
var queue = [];
// 一旦有任务进行就置为true,直到本次队列清空才置为false
var flushing = false;
//requestFlush是一个方法,依据浏览器支持的特性不同会选择不同的API去实现,它会尝试清空任务队列。
var requestFlush;
//标记任务队列中下一个任务的位置
var index = 0;
// 防止内存耗尽,任务队列将定期 //截断已经完成的任务,capacity是任务的最大容量
var capacity = 1024;

flush 清空任务队列

function flush() {
    while (index < queue.length) {
        var currentIndex = index;
        index = index + 1;
        queue[currentIndex].call();
        if (index > capacity) {
            for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
                queue[scan] = queue[scan + index];
            }
            queue.length -= index;
            index = 0;
        }
    }
    queue.length = 0;
    index = 0;
    flushing = false;
}

可以看到flush 用while循环处理任务队列,直到任务队列为空。任务执行完之后把queue等变量重置。

在flush运行过程中如果有其他地方调用了asap,会将任务加入queue,也就是说queue是动态添加的,但是flush只会运行一次,因为rawAsap保证了requestFlush方法只能调用一次

requestFlush是如何实现的?

requestFlush 是基于MutationObserver实现的,如果浏览器不支持该API,会选择使用定时器实现

var scope = typeof global !== "undefined" ? global : self;
var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver;
if (typeof BrowserMutationObserver === "function") {
    requestFlush = makeRequestCallFromMutationObserver(flush);
}else{
    requestFlush = makeRequestCallFromTimer(flush);
}

makeRequestCallFromMutationObserver

创建一个空的文本节点,监听其characterData属性的变化,修改node.data属性触发callback

function makeRequestCallFromMutationObserver(callback) {
    var toggle = 1;
    var observer = new BrowserMutationObserver(callback);
    var node = document.createTextNode("");
    observer.observe(node, {characterData: true});
    return function requestCall() {
        toggle = -toggle;
        node.data = toggle;
    };
}

makeRequestCallFromTimer

function makeRequestCallFromTimer(callback) {
    return function requestCall() {
        var timeoutHandle = setTimeout(handleTimer, 0);
        var intervalHandle = setInterval(handleTimer, 50);

        function handleTimer() {
            clearTimeout(timeoutHandle);
            clearInterval(intervalHandle);
            callback();
        }
    };
}

MessageChannel 实现任务调度

通过消息通道也可以实现,但是该Api有些浏览器不支持

function makeRequestCallFromMessageChannel(callback) {
     var channel = new MessageChannel();
     channel.port1.onmessage = callback;
     return function requestCall() {
         channel.port2.postMessage(0);
     };
 }

node环境实现方式一样,只是异步回调使用的是 process.nextTick和setImmediate

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions