回调函数大家不陌生,但是对于闺蜜们来说就比较陌生了。向女朋友解释回调函数是什么,为什么要这样设计,使用回调函数要注意什么,真的不容易。于是来了这篇文章。在JavaScript中,我个人认为了解回调函数有助于我们理解JavaScript中的其他设计,比如异步编程、消息循环、一些常用的WebAPI、Node中的异步。这些都和回调函数息息相关,所以我们一步步学习和使用吧。废话不多说,直接上干货!什么是回调函数?回调函数也是一种函数。回调函数与普通函数的区别在于它的调用时机。所谓回调函数就是将一个函数作为另一个函数的参数,在这个函数的某个时机调用。我们称这个传入函数为回调函数。回调函数分为同步回调和异步回调。两者的区别在于调用的位置。1.同步回调所谓同步回调函数是在执行的函数内部调用的。例如:functionfn(){console.log("我是同步回调函数");}functionbar(f){f();}bar(fn);我们声明一个回调函数fn,传入bar函数,我们在bar内部调用fn,此时是同步调用。2.异步回调异步回调函数是在执行函数外调用的。如下代码:functionfn(){console.log("我是异步回调函数");}settimeout(fn,1000);上面代码中,settimeout的第一个参数fn是传入的回调函数,当程序执行到1000ms时,此时调用回调函数,是在setTimeout函数之外调用的。我们称fn为异步回调函数。以上JS线程架构的问题,理解起来都非常简单,尤其是同步回调函数。但是我们目前对于何时何地调用异步回调函数非常模糊。所以我们要想知道什么时候在什么地方调用异步回调函数,就需要了解JS的线程架构,比如消息队列,事件循环,从根源上了解JS是如何设计这些东西的。JS最初的设计是单线程的。所谓单线程就是同一时间只能做一件事,JS运行的单线程就是页面的UI线程。毕竟JS可以更方便的操作DOM。那么问题来了,当我们将JS设计到UI页面线程中时,会出现一个问题,当用户通过页面交互产生一个事件时,如果当前线程正在处理其他任务,那么这个交互事件需要等待当前UIthread任务处理完了才能处理,这样的设计比较鸡肋。如果当前UI线程一直在执行其他任务,用户的交互事件任务还没有处理完,就会出现无效页面点击的错觉。为了解决这个问题,GoogleV8团队引入了消息队列。消息队列有了消息队列,我们??把所有产生的事件,不管是JavaScript产生的事件,还是用户点击页面交互产生的事件,依次放入消息队列,然后UI线程会不断循环消息队列,有任务就取出来执行。有了消息队列,主线程就可以有序的执行各种事件任务。什么时候调用异步任务?还是上面setTimeout异步回调函数的例子。如果当前线程从消息队列中取出setTimeout代码执行,发现1000ms后执行了fn任务。1000ms后,主线程会将fn封装成一个事件任务,扔到消息队列中等待执行。当消息队列中执行到一定时间后,会取出封装好的fn回调函数task,在主线程中执行。这个不难理解,但是有一种特殊类型的回调函数。当主线程从消息队列中取出一个网络下载任务时,网络下载是比较耗时的,我们不能让它阻塞主线程中其他任务的执行,所以主线程会交给网络线程执行下载任务,然后主线程会继续从消息队列中取出其他任务有序执行。这时网络线程会收到这个任务,执行下载操作。网络线程下载完文件后,会将下载的结果封装成一个事件任务,放入消息队列中。当主线程执行这个任务时,它知道网络线程已经完成下载,然后将下载的结果呈现给用户。最后,今天主要跟大家分享一下JavaScript中回调函数的由来和异步回调函数的设计。这也是V8引擎的一小部分。接下来,通过这样的知识点,来挖掘一下V8谷歌引擎的各个方面。优秀的设计解决方案。
