摘要:如果你把函数的指针地址作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
同期异步系列文章推荐
谈一谈javascript异步
javascript异步与promise
javascript异步之Promise.all()、Promise.race()、Promise.finally()
javascript异步之Promise.resolve()、Promise.reject()
javascript异步之Promise then和catch
javascript异步之async(一)
javascript异步之async(二)
javascript异步实战
javascript异步总结归档
我们之前介绍了javascript异步的相关内容,我们知道javascript以同步,单线程的方式执行主线程代码,将异步内容放入事件队列中,当主线程内容执行完毕就会立即循环事件队列,直到事件队列为空,当用产生用户交互事件(鼠标点击,点击键盘,滚动屏幕等待),会将事件插入事件队列中,然后继续执行。
处理异步逻辑最常用的方式是什么?没错这就是我们今天要说的---回调
如你所知,函数是对象,所以可以存储在变量中,
所以函数还有以下身份:
可以作为函数的参数
可以在函数中创建
可以在函数中返回
当一个函数a以一个函数作为参数或者以一个函数作为返回值时,那么函数a就是高阶函数
回调函数
百度百科
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
维基百科
在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。
回调函数,几乎每天我们都在用
setTimeout(() => { console.log("这是回调函数"); }, 1000); const hero=["郭靖","黄蓉"] hero.forEach(item=>{ console.log(item); })回调函数解决了哪些问题
举一个简单的:
let girlName = "裘千尺" function hr() { girlName = "黄蓉" console.log(`我是${girlName}`); } function gj() { console.log(`${girlName}你好,我是郭靖,认识一下吧`); } hr() gj()
输出,重点看输出顺序
//=>我是黄蓉 //=>黄蓉你好,我是郭靖,认识一下吧
上面的代码输出是没什么悬念的,不存在异步,都单线程同步执行,最后郭靖和黄蓉相识
如果这时候黄蓉很忙,出现了异步,会怎么样?
let girlName = "裘千尺" function hr() { setTimeout(() => { girlName = "黄蓉" console.log("我是黄蓉"); }, 0); } function gj() { console.log(`${girlName}你好,我是郭靖,认识一下吧`); } hr() gj()
输出,重点看输出顺序
//=>裘千尺你好,我是郭靖,认识一下吧 //=>我是黄蓉
虽然定时器是0ms,但是也导致了郭靖和黄蓉的擦肩而过,这不是我们期望的结果,hr函数存在异步,只有等主线程的内容走完,才能走异步函数
所以最简单的办法就是使用回调函数解决这种问题,gj函数依赖于hr函数的执行结果,所以我们把gj作为hr的一个回调函数
let girlName = "裘千尺" function hr(callBack) { setTimeout(() => { girlName = "黄蓉" console.log("我是黄蓉"); callBack() }, 0); } function gj() { console.log(`${girlName}你好,我是郭靖,认识一下吧`); } hr(gj)
输出,重点看输出顺序
//=>我是黄蓉 //=>黄蓉你好,我是郭靖,认识一下吧
⚠️:当回调函数作为参数时,不要带后面的括号!我们只是传递函数的名称,不是传递函数的执行结果
上面小栗子貌似的很简单,我们继续
我们把昨天的demo做一下升级
引入了lodash:处理按钮点击防抖
axios,集成了promis,但promise不是我们今天讨论的内容,我们只使用axios的ajax请求接口功能
easy-mock:接口数据,用来实现ajax请求(数据是假的,但是请求是真的)
javascript回调
仔细看代码,不难发现,这是一个典型的嵌套回调,我们分析一下
第一层异步,用户交互,来自按钮的点击事件
第二层异步,按钮去抖,来自lodash下debounce的500ms延时
第三次异步,ajax请求,处理后台接口数据
拿到数据后我们没有继续做处理,在实际工作中可能还存在异步,还会继续嵌套,会形成一个三角形的缩进区域
再继续嵌套,就会形成所说的“回调地狱”,就是回调的层级太多了,代码维护成本会高很多
上面的栗子最多算是入门毁掉地狱,我们看一下这个
function funA(callBack) { console.log("A"); setTimeout(() => { callBack() }, 10); } function funB() { console.log("B"); } function funC(callBack) { console.log("C"); setTimeout(() => { callBack() }, 100); } function funD() { console.log("D"); } function funE() { console.log("E"); } function funF() { console.log("F"); } //从这里开始执行 funA(() => { funB() funC(() => { funD() }) funE() }) funF()
(这段代码,带回调的都是异步逻辑)你能很快的看出这段代码的执行顺序吗?
顺序如下:A、F、B、C、E、D
一般正常人不会这么嵌套多层,层级一多,就会考虑拆分
const btn = document.querySelector("button") //监听按钮点击事件 btn.onclick = () => { debounceFun() } //去抖动 const debounceFun = _.debounce(() => { ajax() }, 500) //ajax 请求 const ajax = function () { axios.get("https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/mock") .then(data => { console.log("ajax返回成功"); myData = data.data console.log(myData); }) .catch(error => { console.log("ajax返回失败"); }) }
我相信很多人都会通过这种链式回调的方式处理异步回调,因为可读性比嵌套回调要搞,但是维护的成本可能要高很多
上面的栗子,三个异步函数之间只有执行顺序上的关联,并没有数据上的关联,但是实际开发中的情况要比这个复杂,
我们举一个简单的栗子
let girlName = "裘千尺" function hr(callBack) { setTimeout(() => { girlName = "黄蓉" console.log("我是黄蓉"); callBack(girlName) }, 0); } function gj(love) { console.log(`${girlName}你好,我是郭靖,认识一下吧,我喜欢${love}`); } hr(gj)
gj作为hr的回调函数,并且hr将自己的一个变量传递给gj,gj在hr的回调中执行,
仔细看这种写法并不严谨,
如果gj并不只是一个function类型会怎么样?
如果love的实参并不存在会怎么样?
况且这只是一个简单的栗子
所以回调函数中,参数的校验是很有必要的,回调函数链拉的越长,校验的条件就会越多,代码量就会越多,随之而来的问题就是可读性和可维护性就会降低。
但我们引用了第三方的插件或库的时候,有时候难免要出现异步回调的情况,一个栗子:
xx支付,当用户发起支付后,我们将自己的一个回调函数,传递给xx支付,xx支付比较耗时,执行完之后,理论上它会去执行我们传递给他的回调函数,是的理论上是这样的,我们把回调的执行权交给了第三方,隐患随之而来
第三方支付,多次调用我们的回调函数怎么办?
第三方支付,不调用我们的回调函数怎么办?
当我们把回调函数的执行权交给别人时,我们也要考虑各种场景可能会发生的问题
总结一下:
回调函数简单方便,但是坑也不少,用的时候需要多注意校验
原文链接
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/101162.html
摘要:到这里,我已经发出了一个请求买汉堡,启动了一次交易。但是做汉堡需要时间,我不能马上得到这个汉堡,收银员给我一个收据来代替汉堡。到这里,收据就是一个承诺保证我最后能得到汉堡。 同期异步系列文章推荐谈一谈javascript异步javascript异步中的回调javascript异步之Promise.all()、Promise.race()、Promise.finally()javascr...
摘要:调用栈被清空,消息队列中并无任务,线程停止,事件循环结束。不确定的时间点请求返回,将设定好的回调函数放入消息队列。调用栈执行完毕执行消息队列任务。请求并发回调函数执行顺序无法确定。 异步编程 JavaScript中异步编程问题可以说是基础中的重点,也是比较难理解的地方。首先要弄懂的是什么叫异步? 我们的代码在执行的时候是从上到下按顺序执行,一段代码执行了之后才会执行下一段代码,这种方式...
摘要:异步本质上应该就是多线程语言的产物。如果是多线程的异步,假死的应该是运行方法的线程,而方法仍然会按预期打印出。当然了,按我个人的理解,应该说是是的回调函数。 引子 每个故事都有由来。前两天在看 gulp 的时候,看到了它有个 promise 的玩意儿,然后的然后,这两天就掉进了 javascript 的异步和回调的坑里面去了。 其间搜索了 javascript promise,看到了...
摘要:回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。同步方式请求异步同步请求当请求开始发送时,浏览器事件线程通知主线程,让线程发送数据请求,主线程收到 一直以来都知道JavaScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况。一般被问到异步的时候脑子里第一反应就是Ajax,setTimse...
摘要:最受欢迎的引擎是,在和中使用,用于,以及所使用的。怎么处理每个引擎都有一个基本组件,称为调用栈。也就是说,如果有其他函数等待执行,函数是不能离开调用栈的。每个异步函数在被送入调用栈之前必须通过回调队列。例如方法是在中传递的回调函数。 翻译:疯狂的技术宅 原文:www.valentinog.com/blog/engine… 从Call Stack,Global Me...
阅读 997·2021-10-19 11:42
阅读 2953·2021-09-10 10:51
阅读 651·2021-09-09 09:33
阅读 1744·2021-09-01 10:43
阅读 2746·2019-08-30 12:43
阅读 3501·2019-08-30 11:24
阅读 2075·2019-08-30 10:56
阅读 2763·2019-08-29 11:00