EventLoop
EventLoop本质上就是事件循环机制
先探讨EventLoop在前端的应用
地球人都知道Javascript语言的一大特点就是单线程,这意味这它同一时间只能干一件事,特别的专一。而单线程就意味着所有任务都得排队来,前一个任务干完了才能执行下一个任务。 后来设计者发现,假如上个任务是个响应速度很慢的Ajax请求,那下一个任务不得干等着完犊子了嘛。于是乎,所有任务被分为同步任务和异步任务。具体来说,js的执行机制如下:
- 所有同步任务在主线程上执行
- 主线程之外,还存在个任务队列,异步任务有了运行结果,就会在任务队列中放置一个事件
- 执行栈中所有的
同步任务
执行完毕,就会从任务队列中取事件进入执行栈开始执行 - 主线程不断重复第三步
主线程从任务队列中读取事件的过程是不断循环的,所以整个运行机制又称为Event Loop
同步任务指:函数方法中:除了异步调用之外的代码,称之为同步任务
例如:
// 同步任务
console.log('1');
// 异步任务
setTimeout(function() {
console.log('2')
});
// 同步任务
console.log('3');
运行结果:同步任务优先执行,异步代码最后执行。结果:1,3,2
微任务与宏任务
假如js按照真正的单线程执行的话,一旦遇到任务繁多的情况,势必执行效率会下降。为了解决这个问题,就需要利用微任务和宏任务来模拟“多线程”提高执行效率。通常我们把由宿主发起的任务称为宏任务,由JavaScript引擎发起的任务称为微任务
常见宏任务:setTimeout(),setInterval(),setImmediate()
常见微任务:Promise().then(function(){}),new MutationObserver(),process.nextTick()
需要注意的是Promise()这个构造函数属于同步任务,而.then()里面的函数才会被推入到微任务队列里。
setTimeout(function () {
console.log(1);
});
new Promise(function (resolve, reject) {
console.log(2);
resolve(3);
}).then(function (val) {
console.log(val);
console.log(4);
});
//2 3 4 1
按照JS的执行顺序是先执行主线程上代码,当同步任务执行完毕,执行栈清空后,再去微任务队列里取微任务执行,当执行栈为空后,我们再去宏任务队列中取宏任务执行。 那么我们从上往下看,首先setTimeout是个宏任务,那么我们把它推入到宏任务队列里,下面Promise的构造函数则属于同步任务,那么我们在控制台上打印出个2,resolve(3)我们会传递给then()方法里的回调,而.then()里面的函数属于微任务,则把其推入到微任务队列里,则得出2 3 4 1的结果。
总结一下事件循环闭环流程
当主线程同步任务执行完毕,执行栈为空时,执行以下步骤
-
执行微任务队列 a. 选择微任务队列中最早的任务(任务X)
b. 如果微任务队列为空,则跳转到步骤g
c. 将当前正在运行的任务设置为任务X
d. 运行任务X
e. 将当前正在运行的任务设置为空,删除任务X
f. 选择微任务队列中下一个最早的任务,跳转到步骤b
g. 完成微任务队列
-
选择宏任务队列中最早的任务(任务Y)
-
将当前正在运行的任务设置为任务Y
-
运行任务Y,先执行其中的同步任务
-
跳转到步骤1
-
将当前正在运行的任务设置为空,删除任务Y结束本次Loop循环
-
跳转到步骤2
Flutter中的宏任务与微任务
常见的宏任务:Timer、Future、Future.delayed、async/await
常见的微任务:scheduleMicrotask、Future.microtask、SchedulerBinding.instance.addPostFrameCallback、WidgetsBinding.instance.addPostFrameCallback
需要注意的是:WidgetsBinding.instance.addPostFrameCallback 与 SchedulerBinding.instance.addPostFrameCallback虽然也是微任务,但是他们两者的调用是在widget构建已完成之后,才执行.
测试一下大家弄明白宏任务与微任务没。试着推敲一下执行的顺序
SchedulerBinding.instance.addPostFrameCallback((_) {
print("SchedulerBinding");
});
WidgetsBinding.instance.addPostFrameCallback((_) {
print("WidgetsBinding");
});
Timer.run(() {
print("Timer");
});
scheduleMicrotask(() {
print("scheduleMicrotask");
});
Future<void>.microtask(() {
print("Future Microtask");
});
Future<void>(() {
print("Future");
Future<void>.microtask(() {
print("Microtask from Event");
});
});
Future<void>.delayed(Duration.zero, () {
print("Future.delayed");
Future<void>.microtask(() {
print("Microtask from Future.delayed");
});
});
输出结果如下:
I/flutter (31989): scheduleMicrotask
I/flutter (31989): Future Microtask
I/flutter (31989): SchedulerBinding
I/flutter (31989): WidgetsBinding
I/flutter (31989): Timer
I/flutter (31989): Future
I/flutter (31989): Microtask from Event
I/flutter (31989): Future.delayed
I/flutter (31989): Microtask from Future.delayed
为什么要设计微任务
总的来说,微任务解决了宏任务执行时机不可控的问题。 假设我们只有主线程和宏任务,那么当主线程上的同步任务执行完成之后,就会去宏任务队列里去取事件放到主线程的执行栈中去执行,此时宏任务A要执行多久我们就没办法控制,假如宏任务B中有一个特别紧急的操作,要进行UI的渲染的话就没法及时操作,只能在这里干等着,听天由命。而引入微任务后就会发生改变,我们可以把这些特别紧急的操作放到微任务队列里去。