1.1 定义
回调是指将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行, 其本质上是一个高阶函数。高阶函数至少满足下列一个条件:
- 接受一个或多个函数作为输入
- 输出一个函数
如:JS中的 map方法
注意,这里仅仅传递了**函数定义,我们并没有在参数中**执行函数。仅是传递cb,而不是cb()。
同时,回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”
和普通函数的区别
回调函数区别于普通函数,在于它的调用方式。只有当某个函数被作为参数,传递给另外一个函数,或者传递给宿主环境,然后该函数在函数内部或者在宿主环境中被**调用**,才称为回调函数。
使用
在执行之前确保回调函数是一个函数
在调用之前检查作为参数被传递的回调函数确实是一个函数
function getInput(options, callback){//确保callback是一个函数if(typeof callback === "function"){//调用它,既然我们已经确定了它是可调用的callback(options);}}
回调函数的this问题
由于传递回调函数,仅是传递了其定义,并未立即调用;所有回调函数中的this最终由其调用对象决定的。
在下面代码中,client.setUsername仅是传递了一个函数定义,client 并不是setUsername的调用者。
var client = {setUserName: fucntion (args){this.fullName = firstName + " " + lastName;}}function getUserInput(callback){callback(args);}getUserInput("Barack","Obama",client.setUserName);
使用Call和Apply函数来保存this
// client对象会被Apply方法使用来设置this对象function getUserInput(firstName, lastName, callback. callbackObj){callback.apply(callbackObj, [firstName, lastName]);}getUserName("Barack", "Obama", client.setUserName, client);
1.2 同步回调与异步回调
回调函数有两种不同的形式,同步回调和异步回调。
两者最大区别在于:
- 同步回调函数是在其执行函数内部被执行的,
- 异步回调函数是在执行函数外部被执行的。
下面代码中,callback 是在 doWork 函数内部执行,所以这是一个同步回调。
// 回调函数let callback = function(){console.log('i am do homework')}// 执行函数function doWork(cb) {console.log('start do work')cb()console.log('end do work')}// 调用执行函数doWork(callback)
异步回调,函数并不是在它的执行函数内部被执行的,而是在其他的位置和其他的时间点被执行的,
function foo() {alert("Hello");}setTimeout(foo, 3000)
V8 执行setTimeout 时,会立即返回 ; 等待 3000 毫秒之后,foo 函数才会被 V8 调用。
foo 函数并不是在 setTimeout 函数内部被执行的,所以这是一个异步回调。
- 对于同步回调函数的执行时机,理解起来比较简单,就是回调函数在执行函数内部被执行;
- 而异步回调函数在什么时机和什么位置执行,并又不是都能由我们决定的,其可能存在特定的API,在执行到特定步骤时,由浏览器自己调用。
XHR处理回调
使用xhr.send来发起网络请求之后:
- 渲染进程会将请求发送给网络进程,然后网络进程负责资源的下载;
- 等网络进程接收到数据之后,就会利用
IPC来通知渲染进程; - 渲染进程接收到消息之后,会将 xhr 返回的状态封装成任务添加都任务队列中;
- 等主线程循环系统执行到该任务的时候, 就会根据相关的状态来调用对应的回调函数。
- 如果网络请求出错了,就会执行
xhr.onerror; - 如果超时了,就会执行
xhr.ontimeout; - 如果是正常的数据接收,就会执行
onreadystatechange来反馈相应的状态。
- 如果网络请求出错了,就会执行
setTimeOut的实现
Node处理文件读取
当遇到I/O操作时,Node代码在发起一个系统调用后继续向下执行,操作完成后,再执行对应的回调函数(异步)
