JavaScript运行机制
Event Loop机制
js引擎遇到一个异步任务后并不会一直等待其返回结果,而是会将这个任务交给浏览器的其他模块进行处理(以webkit为例,是webcore模块),继续执行调用栈中的其他任务。当一个异步任务返回结果后,js引擎会将这个任务加入与当前调用栈不同的另一个队列,我们称之为事件队列。
任务队列和事件循环
JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)
首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。
JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。
宏观任务(macro-task)、微观任务(micro-task)
宏任务(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
转载自:https://juejin.cn/post/6883650537124790279