浏览器主进程
渲染进程
就是我们说的浏览器内核网络进程
处理网络请求、文件访问等操作(function mainThread() {
let task1 = 1 + 2;
let task2 = 2 + 3;
let task3 = 3 + 4;
console.log(task1, task2, task3);
})()
let readline = require('readline-sync');
(function mainThread() {
while (true) {
var num1 = readline.question('num1: ');
var num2 = readline.question('num2: ');
let ret = eval(num1 + "+" + num2);
console.log(ret);
}
})();
class MessageQueue {
constructor() {
this.messages = [];
}
put(message) {
this.messages.push(message);
}
get() {
return this.messages.pop();
}
}
module.exports = new MessageQueue();
const messageQueue = require('./messageQueue');
(function mainThread() {
setInterval(() => {
let task = messageQueue.get();
if (task) task();
}, 1000);
})();
(function IOThread() {
let counter = 1;
setInterval(() => {
messageQueue.put(()=>console.log(`task` + counter++));
}, 1000);
})();
render.js
const { fork } = require('child_process');
const messageQueue = require('./messageQueue');
(function mainThread() {
setInterval(() => {
let task = messageQueue.get();
if (task) task();
}, 1000);
})();
(function IOThread() {
let browser = fork('./browser.js');
browser.on('message', function ({ data }) {
messageQueue.put(() => {
console.log(data);
});
});
browser.send({ type: 'click', data: 'clicked' });
})();
process.on('message', function ({ data }) {
setTimeout(() => {
process.send({ data });
}, 100);
});
render.js
const { fork } = require('child_process');
+const { macroTaskQueue, microTaskQueue } = require('./messageQueue');
(function mainThread() {
setInterval(() => {
+ let macroTask = macroTaskQueue.get();
+ if (macroTask) macroTask();
+ let microTask;
+ while (microTask = microTaskQueue.get()) {
+ microTask();
+ }
}, 1000);
})();
(function IOThread() {
let browser = fork('./browser.js');
browser.on('message', function ({ data }) {
macroTaskQueue.put(() => {
console.log(data);
+ microTaskQueue.put(() => {
+ console.log('microTask1');
+ microTaskQueue.put(() => {
+ console.log('microTask2');
+ });
+ });
});
});
browser.send({ type: 'click', data: 'clicked' });
})();
browser.js
//浏览器主进程
process.on('message', function ({ data }) {
setTimeout(() => {
process.send({ data });
}, 100);
});
messageQueue.js
class MessageQueue {
constructor() {
this.messages = [];
}
put(message) {
this.messages.push(message);
}
get() {
return this.messages.pop();
}
}
+exports.macroTaskQueue = new MessageQueue();
+exports.microTaskQueue = new MessageQueue();
DelayTask.js
let timerCounter = 1;
class DelayTask {
constructor(callback, delayTime) {
this.id = timerCounter++;
this.startTime = Date.now();
this.callback = callback;
this.delayTime = delayTime;
}
}
module.exports = DelayTask;
render.js
const { fork } = require('child_process');
const { macroTaskQueue, microTaskQueue } = require('./messageQueue');
+const DelayTask = require('./DelayTask');
+let delayTaskQueue = [];
(function mainThread() {
setInterval(() => {
let macroTask = macroTaskQueue.get();
if (macroTask) macroTask();
+ processDelayTask();
let microTask;
while (microTask = microTaskQueue.get()) {
microTask();
}
}, 16);
})();
+function setTimeout(callback, delayTime) {
+ delayTaskQueue.push(new DelayTask(callback, delayTime));
+}
+function clearTimeout(timeId) {
+ delayTaskQueue = delayTaskQueue.filter(delayTask => {
+ return delayTask.id !== timeId;
+ });
+}
+function processDelayTask() {
+ delayTaskQueue = delayTaskQueue.filter(delayTask => {
+ const { callback, startTime, delayTime } = delayTask;
+ if (Date.now() > startTime + delayTime) {
+ macroTaskQueue.put(callback);
+ return false;
+ }
+ return true;
+ });
+}
(function IOThread() {
let browser = fork('./browser.js');
+ console.time('cost');
browser.on('message', function ({ data }) {
console.log(data);
+ setTimeout(() => {
+ console.timeEnd('cost');
+ }, 1000);
});
browser.send({ type: 'click', data: 'clicked' });
})();
XMLHttpRequest
是由浏览器进程或发起请求,然后再将执行结果利用 IPC
的方式通知渲染进程,之后渲染进程再将对应的消息添加到消息队列中render.js
const { fork } = require('child_process');
const { macroTaskQueue, microTaskQueue } = require('./messageQueue');
(function mainThread() {
setInterval(() => {
let macroTask = macroTaskQueue.get();
if (macroTask) macroTask();
let microTask;
while (microTask = microTaskQueue.get()) {
microTask();
}
}, 16);
})();
(function IOThread() {
let browser = fork('./browser.js');
console.time('cost');
browser.on('message', function () {
+ let xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://localhost:3000/data');
+ xhr.onload = function () {
+ console.log(xhr.response);
+ }
+ xhr.send();
});
browser.send({ type: 'click', data: 'clicked' });
})();
+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;
+ macroTaskQueue.put(this.onload);
+ }
+ });
+ child.send({ type: 'send', options: this.options });
+ }
+}
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();
}
});
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("服务已经在3000端口启动!")
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<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) {
console.log(mutation);
if (mutation.type == 'childList') {
console.log('添加或删除子节点');
console.log(mutation.addedNodes);
console.log(mutation.removedNodes);
} 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>
</html>
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')
})
})
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
let fs = require('fs');
setTimeout(() => {
console.log('1');
let rs1 = fs.createReadStream(__filename);
rs1.on('data', () => {
rs1.destroy();
setImmediate(() => console.log('setImmediate_a'));
setTimeout(() => {
console.log('setTimeout_a')
});
console.log('a');
});
rs1.on('close', () => console.log('end_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);
const Promise = require('./Promise');
Promise.resolve().then(() => {
console.log(0);
return new Promise((resolve)=>{
resolve('a');
})
}).then(res => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
})
// 0 1 2 3 a 4 5
const Promise = require('./Promise');
let promise1 = Promise.resolve();
let promise2 = promise1.then(() => {
console.log(0);
let promise10 = Promise.resolve('a');
return promise10;
})
let promise3 = promise2.then(res => {
console.log(res)
})
let promise4 = Promise.resolve();
let promise5 = promise4.then(() => {
console.log(1);
});
let promise6 = promise5.then(() => {
console.log(2);
});
let promise7 = promise6.then(() => {
console.log(3);
});
let promise8 = promise7.then(() => {
console.log(4);
});
let promise9 = promise8.then(() => {
console.log(5);
})
// 0 1 2 3 a 4 5
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
function resolvePromise(promise, x, resolve) {
if (x && typeof x.then === 'function') {
queueMicrotask(() => {
console.log('resolvePromise: microtask', x.id);
x.then(y => resolvePromise(promise, y, resolve));
});
} else {
resolve(x)
}
}
class Promise {
static counter = 1;
constructor(executor) {
this.id = Promise.counter++;
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
const resolve = (value) => {
if (value instanceof Promise) {
return queueMicrotask(() => {
console.log('resolve: microtask', value.id);
value.then(resolve)
});
}
if (this.status == PENDING) {
this.value = value;
this.status = FULFILLED
this.onResolvedCallbacks.forEach(cb => cb(this.value))
}
}
executor(resolve);
}
then(onFulfilled) {
let newPromise = new Promise((resolve) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
console.log('FULFILLED then: microtask', this.id);
let x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve)
})
}
if (this.status == PENDING) {
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve)
})
});
}
});
return newPromise;
}
static resolve(value) {
return new Promise((resolve) => {
resolve(value)
})
}
}
module.exports = Promise;
console.log('FULFILLED then: microtask', this.id);
let x = onFulfilled(this.value);//x=k
resolvePromise(p1, x, resolve, reject)
然后继续调用promise2的then方法,发现promise2还处于等待态,不能直接添加微任务,只能添加
() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);
resolvePromise(p1, x, resolve)
})
}
到promise2的onResolvedCallbacks的尾部,并返回promise3,这个promise3后面没有再用到了,此时第一段代码结束
开始执行第二段代码
queueMicrotask(() => {
console.log('FULFILLED then: microtask',4);
let x = onFulfilled(this.value);//x=k
resolvePromise(newPromise, x, resolve)
})
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);// console.log(2);
resolvePromise(newPromise, x, resolve)
})
});
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);// console.log(3);
resolvePromise(newPromise, x, resolve)
})
});
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);// console.log(4);
resolvePromise(newPromise, x, resolve)
})
});
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);// console.log(5);
resolvePromise(newPromise, x, resolve)
})
});
然后继续调用promise9的then方法,发现promise9还处于等待态,不能直接添加微任务,只能添加
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
console.log('PENDING then: microtask', this.id);
let x = onFulfilled(this.value);// console.log(6);
resolvePromise(newPromise, x, resolve)
})
});
此时,任务列队上有两个新任务
微任务队列 [FULFILLED then: microtask 1
,FULFILLED then: microtask 4
]
FULFILLED then: microtask 1
执行resolvePromise(promise2, promise10, resolve)
的时候,如果发现promise10是一个promise,并且入队console.log('resolvePromise: microtask 10');
x.then(y => resolvePromise(promise, y, resolve));
FULFILLED then: microtask 4
,resolvePromise: microtask 10
]FULFILLED then: microtask 4
,输出1PENDING then: microtask 5
入队,此时本任务结束resolvePromise: microtask 10
,PENDING then: microtask 5
]resolvePromise: microtask 10
,因为promise10是直接成功的,直接执行成功回调,入队console.log('FULFILLED then: microtask 10');
let x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve)
PENDING then: microtask 5
,FULFILLED then: microtask 10
]PENDING then: microtask 5
,输出2PENDING then: microtask 6
入队,此时本任务结束FULFILLED then: microtask 10
,PENDING then: microtask 6
]FULFILLED then: microtask 10
,它会让promise2变成成功态,并且把promise2的成功回调入队PENDING then: microtask 6
,FULFILLED then: microtask 2
]PENDING then: microtask 6
,输出3,PENDING then: microtask 7
入队,此时本任务结束FULFILLED then: microtask 2
,PENDING then: microtask 7
]FULFILLED then: microtask 2
,输出aPENDING then: microtask 7
,输出4PENDING then: microtask 8
入队,此时本任务结束PENDING then: microtask 8
]PENDING then: microtask 8
,输出5FULFILLED then: microtask 1
0
FULFILLED then: microtask 4
1
resolvePromise: microtask 10
PENDING then: microtask 5
2
FULFILLED then: microtask 10
PENDING then: microtask 6
3
PENDING then: microtask 2
a
PENDING then: microtask 7
4
PENDING then: microtask 8
5
const Promise = require('./Promise');
Promise.resolve().then(() => {
console.log(0);
return new Promise((resolve) => {
resolve(new Promise((resolve) => {
resolve('a');
}));
})
}).then(res => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
})
// 0 1 2 3 4 a 5