意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

js中的回调函数,你有想过吗?

来源:恒创科技 编辑:恒创科技编辑部
2024-02-03 14:01:59


前言

前段时间腾讯三面(没看清要求,好像那个岗也要了解后端知识比如Redis但是我不会,已挂),有一个前端知识把我问懵了:请讲一下js中的回调函数,回调函数是什么?
讲真,一直在用回调但是却压根没有想过,确实是我本身的不足。


正文

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数的时候,我们就说这是回调函数。
函数指针,也就是函数的地址,可以看做是指向函数的指针变量。


js中的回调函数,你有想过吗?

函数指针有两个用途:调用函数和做(别的)函数的参数!

官方对回调函数的定义是:作为参数传递给另一个函数并在其父函数执行完成后执行的函数。
可以明确的是:回调函数不是由该函数的实现方直接调用,而是在特定的事件/条件下由另外一方调用的,用于对该事件/条件进行响应。

这里不得不提到一个概念:回调队列(也有叫“消息队列”) —— js在运行时除了函数调用栈之外,还包含了一个待处理的回调队列。其中都是已经有了运行结果的异步任务,每一个任务都会关联一个回调函数。
回调队列遵循FIFO(先进先出)的原则,在js代码执行中会进行一些处理:

运行时,会从最先进入队列的任务开始,处理队列中的任务;被处理的任务会被移出队列,该任务的运行结果会作为输入参数,并调用与之关联的函数,此时会产生一个函数调用栈;函数会一直处理调用栈直到再次为空,然后 eventLoop 会去处理队列中的下一个任务

说起回调,就不得不提起“回调地狱” —— 为了达到某种效果而不断在函数中添加函数参数。回调地狱主要有两个问题:

多层嵌套;每种任务的处理结果都存在两种可能性(成功或失败),那么需要在每种任务执行结束后分别处理这两种可能性。

es6用 Promise 解决回调地狱,本质上就是为了解决上面这两个问题。promise里有三大亮点:
1、 回调函数延迟绑定:回调函数不是直接声明的,而是通过后面的 then 方法的调用才传入的:

let readFile=filename=>{
return new Promise((resolve,reject)=>{
fs.readFile(filename,(err,data)=>{
if(err){
reject(err);
}else{
resolve(data);
}
})
})
}
readFile('mxc.json').then(data=>{
return readFile('mxc2.json');
})

2、 返回值穿透:我们根据 then 中回调函数的传入值创建不同类型的 Promise,然后把返回的 Promise 穿透到外层,以供后续调用 —— 也就是promise的“链式调用”:
3、 错误冒泡:依靠 promise 穿透的特点,我们可以把前面产生的错误一直向后传递,直到末尾被 catch 接收,这样就不需要频繁地检查错误了。也不会因为前面的错误阻塞一些代码的执行:

readFile('mxc.json').then(data=>{
return readFile('mxc2.json');
}).then(data=>{
return readFile('mxc3.json');
}).then(data=>{
return readFile('mxc4.json');
}).catch(err=>{
// xxx
})

是的,promise 也是基于回调的


promise如何保证顺序执行?

上面也说了,promise采用了延迟挂载的方式。而且你不知道当前promise的状态是pending、resolved还是rejected。那如果then注册的一套回调中如果既有同步任务也有异步任务,怎么保证他们按顺序执行呢?
关于此,promise/A+ 规范中提到:

onFulfilled or onRejected must not be called until the execution context stack contains only platform code.

简单来说就是:promise强制then方法必须是异步的!我们不是在 JS 引擎层面实现 Promises,而是使用 JS 去实现 JS Promises。在JS里无法主动控制自身 execution context stack。可以通过 ​​setTimeout/nextTick​​ 等 API 间接实现。

那我们可不可以认为:promise中是利用了“事件循环中异步任务队列会顺序执行”的特点?(如有观点,请不吝留言)

Promise.prototype.then=function(onFulfilled,onRejected){
return new Promise((resolve,reject)=>{
let callback={onFulfilled,onRejected,resolve,reject};

if(this.state==PENDING){
// 万一 promise 还在 pending 的时候就挂了 then 呢?
this.callbacks.push(callback);
}else{
setTimeout(()=>handleCallback(callback,this.state,this.result),0);
}
})
}


上一篇: 小酌Object、Map和WeakMap 下一篇: 手机怎么远程登录云服务器?