1. 进程和线程 #

1.1 进程 #

1.2. 线程 #

2. chrome浏览器进程 #

3. 渲染进程 #

4.eventloop使用 #

4.1 宏任务 #

document.body.style = 'background:red';
document.body.style = 'background:yellow';
document.body.style = 'background:blue';
document.body.style = 'background:red';
setTimeout(function(){
    document.body.style = 'background:yellow'
},0)

4.2 微任务 #

4.2.1 promise #

function loop() {
     Promise.resolve().then(loop);
}
loop();
setTimeout(() => {
    console.log(1);
    Promise.resolve(3).then(data => console.log(data))
}, 0)

setTimeout(() => {
    console.log(2)
}, 0)
<body>
    <a id="link" href="http://www.baidu.com">link</a>
    <script>
        let link = document.getElementById('link');
        const nextTick = new Promise(resolve => {
            link.addEventListener('click', resolve, { once: true });
        });
        nextTick.then(event => {
            event.preventDefault();
            console.log('event.preventDefault()');
        });
        //link.click();
    </script>
</body>

4.2.3 MutationObserver #

<body>
    <div id="tree"></div>
    <button onclick="start()">开始监听</button>
    <button onclick="changeAttribute()">修改属性</button>
    <button onclick="addChild()">添加子节点,3秒后删除</button>
    <script>
        var targetNode = document.getElementById('tree');
        var config = { attributes: true, childList: true, subtree: true };
        var callback = function (mutationsList) {
            for (var mutation of mutationsList) {
                if (mutation.type == 'childList') {
                    console.log('一个子节点被添加或者删除了');
                }
                else if (mutation.type == 'attributes') {
                    console.log('属性 ' + mutation.attributeName + ' 被改变了.');
                }
            }
        };

        var observer = new MutationObserver(callback);
        function start() {
            observer.observe(targetNode, config);
        }
        function changeAttribute() {
            targetNode.setAttribute('data-name', '树');
        }
        function addChild() {
            let child = document.createElement('div');
            child.innerHTML = '子节点';
            targetNode.appendChild(child);
            setTimeout(() => {
                targetNode.removeChild(child);
            }, 2000);
        }
    </script>
</body>

5. EventLoop实现 #

5.1 单线执行 #

let ret1 = 1 + 1;
let ret2 = 2 + 2;
let ret3 = 3 + 3;
let ret4 = 4 + 4;
console.log(ret1, ret2, ret3, ret4);

5.2 事件循环 #

cnpm i readline-sync -S
let readline = require('readline-sync');
while (true) {
    var num1 = readline.question('input num1: ');
    var num2 = readline.question('input num2: ');
    let ret = eval(num1 + "+" + num2);
    console.log(ret);
}

5.3 setTimeout #

5.3.1 setTimeout第一版 #

5.3.1.1 main.js #
const { fork } = require('child_process');
let tasks = [];

function showName() {
    console.log("zhufeng")
}
function setTimeout(callback, timeout) {
    let child = fork('./setTimeout.js');
    child.on('message', function (message) {
        if (message.ready) {
            tasks.push(callback);
        }
    });
    child.send({ type: 'timer', timeout });
}
setTimeout(showName, 1000);
setInterval(() => {
    let task = tasks.shift();
    task && task();
}, 100);
5.3.1.2 setTimeout.js #
process.on('message', function (message) {
    let { type, timeout } = message;
    if (type === 'timer') {
        let end = Date.now() + parseFloat(timeout);
        setInterval(() => {
            if (Date.now() >= end) {
                process.send({ ready: true });
                process.exit();
            }
        }, 100);
    }
});

5.3.2 setTimeout第二版 #

main.js

let tasks = [];

let delayTasks = [];
function showName() {
    console.log("zhufeng");
}
function setTimeout(callback, timeout) {
    delayTasks.push({ callback, start: Date.now(), timeout });
}
setTimeout(showName, 1000);

setInterval(() => {
    let task = tasks.shift();
    task && task();
    delayTasks = delayTasks.filter((item) => {
        if (item.start + item.timeout <= Date.now()) {
            tasks.push(item.callback.bind(item));
            return false;
        }
        return true;
    });
}, 100);

5.3.3 注意事项 #

5.3.3.1 当前任务可能会影响定时器任务的执行 #
<script>
        function task() {
            console.log('zhufeng');
        }
        function exec() {
            setTimeout(task, 0);
            let start = Date.now();
            let end = start + 500;
            while (Date.now() < end) {
                console.log(1);
            }
        }
        exec();
    </script>
5.3.3.2 嵌套的定时器最小间隔为4ms #
<script>
        function task() { setTimeout(task, 0); }
        setTimeout(task, 0);
</script>

5.4 XMLHttpRequest #

5.4.1 main.js #


const { fork } = require('child_process');
let tasks = [];
class XMLHttpRequest {
    constructor() {
        this.options = {};
    }
    open(method, url) {
        this.options.method = method;
        this.options.url = url;
    }
    send() {
        let child = fork('./XMLHttpRequest.js');
        child.on('message', (message) => {
            if (message.type === 'response') {
                this.response = message.data;
                tasks.push(this.onload);
            }
        });
        child.send({ type: 'send', options: this.options });
    }
}
function getData() {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://localhost:3000/data');
    xhr.onload = function () {
        console.log(xhr.response);
    }
    xhr.send();
}
getData();

setInterval(() => {
    let task = tasks.shift();
    task && task();
}, 100);

5.4.2 XMLHttpRequest.js #

let url = require('url');
let http = require('http');
process.on('message', function (message) {
    let { type, options } = message;
    if (type == 'send') {
        let urlObj = url.parse(options.url);
        const config = {
            hostname: urlObj.hostname,
            port: urlObj.port,
            path: urlObj.path,
            method: options.method
        };
        var req = http.request(config, (res) => {
            let chunks = [];
            res.on('data', (chunk) => {
                chunks.push(chunk);
            });
            res.on('end', () => {
                process.send({
                    type: 'response',
                    data: JSON.parse(Buffer.concat(chunks).toString())
                });
                process.exit();
            });
        });
        req.on('error', (err) => {
            console.error(err);
        });
        req.end();
    }
});

5.4.3 server.js #

var http = require("http");
var server = http.createServer();
server.on("request", function (request, response) {
    response.end(JSON.stringify({ message: 'hello' }));
})
server.listen(3000, function () {
    console.log("服务器开启成功")
});

5.5 Promise #

5.5.1 main.js #


const { fork } = require('child_process');
let tasks = [];
let microTasks = []
class XMLHttpRequest {
    constructor() {
        this.options = {};
    }
    open(method, url) {
        this.options.method = method;
        this.options.url = url;
    }
    send() {
        let child = fork('./XMLHttpRequest.js');
        child.on('message', (message) => {
            if (message.type === 'response') {
                this.response = message.data;
                tasks.push(this.onload);
            }
        });
        child.send({ type: 'send', options: this.options });
    }
}


function getData() {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://localhost:3000/data');
    xhr.onload = function () {
        console.log(xhr.response);
    }
    xhr.send();
}
getData();
class Promise {
    constructor(executor) {
        executor(this.resolve);
    }
    then = (onResolve) => {
        this._onResolve = onResolve;
    }
    resolve = (value) => {
        microTasks.push(() => this._onResolve(value));
    }
}
new Promise(function (resolve) {
    resolve('promise1');
}).then(result => console.log(result));
new Promise(function (resolve) {
    resolve('promise2');
}).then(result => console.log(result));
console.log('同步任务');

setInterval(() => {
    let task = tasks.shift();
    task && task();
    microTasks.forEach(task => task());
    microTasks = [];
}, 100);

6.requestAnimationFrame #

6.1 requestAnimationFrame #

  button.addEventListener('click', () => {
            box.style.display = 'none';
            box.style.display = 'block';
            box.style.display = 'none';
            box.style.display = 'block';
            box.style.display = 'none';
            window.getComputedStyle(box).marginLeft;
            box.style.display = 'block';
            box.style.display = 'none';
            box.style.display = 'block';
            box.style.display = 'none';
            box.style.display = 'block';
        });

6.2 requestAnimationFrame #

<body>
    <div style="background: lightblue;width: 0;height: 20px;"></div>
    <button>走你</button>
    <script>
        /**
         * requestAnimationFrame(callback) 由浏览器专门为动画提供的API
         * cancelAnimationFrame(返回值) 清除动画
         * <16.7 丢帧
         * >16.7 跳跃 卡顿
         */
        const div = document.querySelector('div');
        const button = document.querySelector('button');
        let start;
        function progress() {
            div.style.width = div.offsetWidth + 1 + 'px';
            div.innerHTML = (div.offsetWidth) + '%';
            if (div.offsetWidth < 100)
                timer = requestAnimationFrame(progress);
            else
                console.log(Date.now() - start);
        }
        button.onclick = () => {
            div.style.width = 0;
            start = Date.now();
            requestAnimationFrame(progress);
        }
    </script>
</body>

7.requestIdleCallback #

7.1 requestIdleCallback #

var handle = window.requestIdleCallback(callback[, options])
<body>
    <script>
        let task = () => {
            console.log('requestAnimationFrame');
        };

        requestIdleCallback(idleWork, { timeout: 2000 });
        // 任务队列
        const tasks = [
            () => {
                console.log("第一个任务");
                requestAnimationFrame(task);
            },
            () => {
                console.log("第二个任务");
                requestAnimationFrame(task);
            },
            () => {
                console.log("第三个任务");
                requestAnimationFrame(task);
            },
        ];

        function idleWork(deadline) {
            console.log('deadline.timeRemaining()', deadline.timeRemaining());
            while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
                work();
            }

            if (tasks.length > 0)
                requestIdleCallback(idleWork);
        }

        function work() {
            tasks.shift()();
            console.log('执行任务');
        }
    </script>
</body>

8. Node中的EventLoop #

8.1 libuv #

8.2 setImmediate #

setTimeout(function  () {
  console.log('timeout');
},0);
setImmediate(function  () {
  console.log('immediate');
});
const fs = require('fs')
fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0)
    setImmediate(() => {
        console.log('immediate')
    })
})

8.3 process.nextTick #

setTimeout(() => {
    console.log('setTimeout1')
    Promise.resolve().then(function () {
        console.log('promise1')
    })
}, 0)
setTimeout(() => {
    console.log('setTimeout2')
    Promise.resolve().then(function () {
        console.log('promise2')
    })
}, 0)
setImmediate(() => {
    console.log('setImmediate1')
    Promise.resolve().then(function () {
        console.log('promise3')
    })
}, 0)

process.nextTick(() => {
    console.log('nextTick1');
    Promise.resolve().then(() => console.log('promise4'));
    process.nextTick(() => {
        console.log('nextTick2');
        Promise.resolve().then(() => console.log('promise5'));
        process.nextTick(() => {
            console.log('nextTick3')
            process.nextTick(() => {
                console.log('nextTick4')
            })
        })
    })
})
// nextTick1 nextTick2 nextTick3 nextTick4
//promise4 promise5 setTimeout1  promise1 setTimeout2 promise2  setImmediate1 promise3 

8.4 Node.js事件环 #

let fs = require('fs');
setTimeout(() => {
    console.log('1');
    let rs1 = fs.createReadStream('./1.txt');
    rs1.on('close', (data) => console.log('end_a'));
    rs1.on('data', () => {
        rs1.destroy();
        setImmediate(() => console.log('setImmediate_a'));
        setTimeout(() => {
            console.log('setTimeout_a')
        });
        console.log('a');
    });
    console.log('2');
    setImmediate(function () {
        console.log('setImmediate1');
        process.nextTick(() => console.log('nextTick1'));
    });
    setImmediate(function () {
        console.log('setImmediate2');
        process.nextTick(() => console.log('nextTick2'));
    });
    console.log('3');
    setTimeout(() => {
        console.log('setTimeout1');
        process.nextTick(() => {
            console.log('nextTick3')
            process.nextTick(() => console.log('nextTick4'));
        });
    });
    setTimeout(() => {
        console.log('setTimeout2');
    });
    console.log('4');
}, 1000);

9. 面试题 #

9.1. 第1题 #

Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)

9.2. 第2题 #

console.log('script start');

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

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

console.log('script end');

9.3. 第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')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
console.log('script end')
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

9.4. 第4题 #

console.log(1);
setTimeout(() => {
    console.log(2);
}, 10);
new Promise(function (resolve) {
    console.log(3);
    resolve();
    console.log(4);
}).then(function () {
    console.log(5);
});
console.log(6);
requestIdleCallback(() => console.log(7));
requestAnimationFrame(() => console.log(8));

9.5. 第5题 #

const Promise = require('./Promise')
Promise.resolve().then(() => {
    console.log(1);
    return Promise.resolve(Promise.resolve('hello'))
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(4);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
})