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

前言

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


正文

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

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

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

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

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

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

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

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);
		}
	})
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恪愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值