同期异步系列文章推荐
谈一谈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以同步,单线程的方式执行主线程代码,将异步内容放入事件队列中,当主线程内容执行完毕就会立即循环事件队列,直到事件队列为空,当用产生用户交互事件(鼠标点击,点击键盘,滚动屏幕等待),会将事件插入事件队列中,然后继续执行。
处理异步逻辑最常用的方式是什么?没错这就是我们今天要说的---回调

js回调函数

如你所知,函数是对象,所以可以存储在变量中,
所以函数还有以下身份:

  1. 可以作为函数的参数
  2. 可以在函数中创建
  3. 可以在函数中返回

当一个函数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请求(数据是假的,但是请求是真的)

嵌套回调

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>javascript回调</title><script src="https://unpkg.com/axios/dist/axios.min.js"></script><script src="https://cdn.bootcss.com/lodash.js/4.17.11/lodash.min.js"></script>
</head><body><button>点击</button><script>{const btn = document.querySelector('button')btn.onclick = () => {_.debounce(() => {axios.get('https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/mock').then(data => {console.log("ajax返回成功");myData = data.dataconsole.log(myData);}).catch(error => {console.log("ajax返回失败");})}, 500)()}}</script>
</body></html>

仔细看代码,不难发现,这是一个典型的嵌套回调,我们分析一下
第一层异步,用户交互,来自按钮的点击事件
第二层异步,按钮去抖,来自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.dataconsole.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支付比较耗时,执行完之后,理论上它会去执行我们传递给他的回调函数,是的理论上是这样的,我们把回调的执行权交给了第三方,隐患随之而来
第三方支付,多次调用我们的回调函数怎么办?
第三方支付,不调用我们的回调函数怎么办?
当我们把回调函数的执行权交给别人时,我们也要考虑各种场景可能会发生的问题

总结一下:
回调函数简单方便,但是坑也不少,用的时候需要多注意校验

原文链接

javascript异步中的回调相关推荐

  1. 什么是JavaScript中的回调函数?

    This article gives a brief introduction to the concept and usage of callback functions in the JavaSc ...

  2. JavaScript异步精讲,让你更加明白Js的执行流程!

    JavaScript异步精讲,让你更加明白Js的执行流程! 问题点 什么是单线程,和异步有什么关系 什么是 event-loop jQuery的Deferred Promise 的基本使用和原理 as ...

  3. 理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  4. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  5. 【JavaScript】理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  6. javascript异步_JavaScript异步并在循环中等待

    javascript异步 Basic async and await is simple. Things get a bit more complicated when you try to use ...

  7. JavaScript中的回调函数(callback)

    前言 callback,大家都知道是回调函数的意思.如果让你举些callback的例子,我相信你可以举出一堆.但callback的概念你知道吗?你自己在实际应用中能不能合理利用回调实现功能? 我们在平 ...

  8. JavaScript异步编程【中】 -- Promise 详细解析

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在ES6中,新增加了一种异步编程的解决方案Promise,它是一种规范,是一套处理JavaScript异步的机制. Promise的含义 简单来说, ...

  9. JavaScript异步编程【上】 -- 同步和异步、事件循环(EventLoop)、微任务和宏任务、回调函数

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在我们学习JavaScript中,我们知道,JavaScript的执行环境是单线程的.所谓单线程是指一次只能完成一个任务,如果有多个任务,就必须排队 ...

最新文章

  1. 界面交互推荐-25个闪亮创意的404错误页面设计-你从中发现了什么
  2. 【组队学习】【32期】scikit-learn教程
  3. java. xerces转xml_Xerces -C++遇到的xml编码转换问题
  4. Unity游戏开发技巧集锦2.1.3实现效果
  5. [2778]小明的花费预算 (二分查找)SDUT
  6. java 异常_Java学习——异常与异常处理
  7. 面试让你手撕红黑树?30张图带你彻底理解红黑树~
  8. Linux命令解释之rpm
  9. 蓝桥杯2019真题-完全二叉树的权值
  10. MacOs中Docker与宿主机网络互通问题解决
  11. 解决PyQt5程序报错Process finished with exit code -1073740791 (0xC0000409)
  12. 完全搞懂java中的时间戳,时区,日期格式
  13. “蚂蚁牙黑,蚂蚁呀吼”一夜间火遍全网?别忽略了潜在风险
  14. JSP设置Excel表格换行_Excel中快速将阿拉伯数字转化为大写文字的妙招
  15. 如何快速找出电脑里的所有视频\照片\文件
  16. 慕容复的故事告诉我们:应试教育害死人
  17. 爱康科技收购北京碳诺科技 加速公司碳排放战略布局
  18. 微软的服务器图片,微软公有云平台部署CNNIC服务器证书
  19. 深度|为什么一定要从DevOps走向BizDevOps?
  20. 中国黑客群体的真实收入

热门文章

  1. linux php pdo mssql,Linux下PHP连接MSSQL2005/2008
  2. Universal 2nd Factor (U2F) 概述(4)-特定的公私秘钥对
  3. python操作word文档-python操作word
  4. CTO说:怎么成为并做好CTO
  5. c语言中什么是指针初始化,C语言中的“char指针初始化”是什么意思?
  6. oracle dsg ogg,ogg 、 Shareplex和DSG RealSync 对比
  7. 人工智能 7.2机器学习
  8. 数字证书在WEB应用中登录
  9. Java 应用程序的 CPU 使用率飙升原因分析
  10. 高通与华为协商专利授权费体现后者专利实力的上升