《JavaScript权威指南》
MDN web docs
Github:smyhvae/web
作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。
PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。
正文-异步回调的执行时机本篇会讲到一个单线程事件循环机制,但并不是网络上对于 js 执行引擎介绍中的单线程机制,也没有涉及宿主环境浏览器的各种线程,如渲染线程、js 引擎执行线程、后台线程等等这些内容。
严谨来讲,应该不属于 JavaScript 自身的单线程机制,而是宿主对象,如浏览器处理执行 js 代码的单线程事件循环机制。
回到正题,本篇所要讲的,就是类比于 Android 中的主线程消息队列循环机制,来讲讲在 JavaScript 中,如果设置了某个异步任务后,当异步任务执行完成需要回调通知时,这个回调任务的执行时机。
如果还不清楚要讲的是什么,那么先来看个问题:
<script type="text/javascript"> $.ajax({ url: "https://easy-mock.com/mock/5b592c01e4e04f38c7a55958/ywb/is/version/checkVersion", data: {"key": 122}, type: "POST", success: function (data) { console.log("----------success-----------"); //什么时候会执行回调 }, error: function (e) { console.log("----------error-----------"); } }); //... </script>这是用 jQuery 写的 ajax 网络请求的示例,这条请求自然是异步进行的,但当请求结果回来后,会去触发 success 或 error 回调,那么,问题来了:
Q:想过没有,如果请求结果回来后,这个回调的代码是在什么时机会被执行的?是立马就执行吗,不管当前是否正在执行某个函数内的代码?还是等当前的函数执行结束?又或者是?
也许你还没看懂这个问题要问的是什么,没关系,下面举例分析时,会讲得更细,到时你就知道这个问题要问的是什么了。
Android 消息队列循环机制先来看看 Android 中的主线程消息队列循环机制,当然如果你不是从 Android 转前端,那可以跳过这趴:
这张图来自 Android消息机制(一):概述设计架构这篇文章中,我懒得自己画了,借大佬图片一用,如果不允许使用,麻烦告知下,我再来自己画。
在 Android 里有个主线程,因为只能在主线程中进行 UI 操作,所以也叫 UI 线程,这个主线程在应用启动时就进入一个死循环中,类似于执行了 while(true){...} 这样的代码,等到应用退出时,退出该死循环。而死循环之所以不会卡死 CPU,是因为利用了 Linux 的 epoll 机制,通俗的来将,就是,主线程会一直循环往消息队列中取消息执行,如果队列中没有消息,那么会进入阻塞状态,等有新的消息到来时,唤醒继续处理。而阻塞和唤醒就是利用了 Linux 的 epoll 机制。
所以,在 Android 中,打开页面是一个 message,触摸屏幕也是一个 message,message 中指示着当前应该执行的代码段,只有当前的 message 执行结束后,下会轮到下个 message 执行。
所以,在 Android 中的异步任务的回调工作,比如同样异步发起一个网络请求,请求结果回来后,需要回调到主线程中处理,那么这个回调工作的代码段会被封装到 message 中,发送到消息队列中排队,直到轮到它来执行。
而 message 发送到消息队列是基于 Handler 来传输,所以,在 Android 中,如果想要查看 message 是以什么为粒度,查找在哪里通过 Handler 发送了 message 即可。
JavaScript 中的单线程事件循环机制那么,在 JavaScript 中,又是如何处理异步工作的回调任务的呢?
查了一些相关的资料,发现讲的都是 JavaScript 的单线程,事件循环机制等之类理论,但却没看到,事件的粒度是什么?
看完我能理解,JavaScript 也是类似 Android,一样执行了某段类似 while(true){...} 的代码来循环处理事件,但看完我仍旧无法理解,这个事件的粒度是什么,怎么查看事件的粒度?
再举个例子来说明我的疑问好了:
<script type="text/javascript"> console.log("----------1-----------"); $.ajax({ url: "https://easy-mock.com/mock/5b592c01e4e04f38c7a55958/ywb/is/version/checkVersion", data: {"key": 122}, type: "POST", success: function (data) { console.log("----------success-----------"); //什么时候会执行回调 }, error: function (e) { console.log("----------error-----------"); } }); console.log("----------2-----------"); alert("2"); //第一个卡点 console.log("----------2.1-----------"); function A() { console.log("------------2.2-----------"); alert("A"); //第二个卡点 } A(); console.log("---------------2.3---------------") </script> <script type="text/javascript"> console.log("----------3-----------"); </script>