关于JavaScript回调函数的深入理解

由于函数是一等对象,我们可以把一个函数作为参数传递给另一个函数,然后在那个函数内执行,至也可以被那个函数返回,然后再执行

Posted in Javascript onJune 27, 2021

前言

JavaScript回调函数是成为一名成功的 JavaScript 开发人员必须要了解的一个重要概念。但是我相信,在阅读本文之后,你将能够克服以前使用回调方法遇到的所有障碍。

在开始之前,首先要确保我们对函数的理解是扎实的。

快速回顾:JavaScript 函数

什么是函数?

函数是在其中有一组代码的逻辑构件,用来执行特定任务。实际上为了易于调试和维护,函数允许以更有组织的方式去编写代码。函数还允许代码重用。

你只需定义一次函数,然后在需要时去调用它,而不必一次又一次地编写相同的代码。

声明一个函数

现在,让我们看看如何在 javascript 中声明一个函数。

  1. 使用函数的构造函数: 在这种方法中,函数是在“函数”的构造函数的帮助下创建的。从技术上讲,这种方法比使用函数表达式语法和函数声明语句语法去声明函数的方法效率要低。
  2. 使用函数表达式: 通常这种方法与变量分配相同。简而言之,函数主体被视为一个表达式,并且该表达式被分配给一个变量。使用这种语法定义的函数可以是命名函数或匿名函数。
    没有名称的函数被称为匿名函数。匿名函数是自调用的,这意味着它会自动调用起自身。这种行为也称为立即调用的函数表达式(IIFE)。
  3. 使用函数声明: 这种方法是 JavaScript 中常用的老派方法。在关键字“function”之后,你必须指定函数的名称。之后,如果函数接受多个参数或参数,也需要提及它们。虽然这部分是完全可选的。

在函数体中,函数必须将一个值返回给调用方。遇到 return 语句后,该函数将会停止执行。在函数内部,参数将会充当局部变量。

同样,在函数内部声明的变量是该函数的局部变量。局部变量只能在该函数内访问,因此具有相同名称的变量可以轻松地用于不同的函数。

调用一个函数

在下列任何一种情况下,将调用之前声明的函数:

  • 发生事件时,例如,用户单击按钮,或者用户从下拉列表中选择某些选项等等。
  • 从 javascript 代码中调用该函数时。
  • 该函数可以自动调用,我们已经在匿名函数表达式中进行了讨论。

() 运算符调用该函数。

什么是回调函数?

按照 MDN 的描述:回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。

让我用人话解释一下,回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个 JavaScript 函数的函数。这个回调函数会在传给的函数内部执行。

在 JavaScript 中函数被看作是一类对象。对于一类对象,我们的意思是指数字、函数或变量可以与语言中的其他实体相同。作为一类对象,可以将函数作为变量传给其他函数,也可以从其他函数中返回这些函数。

可以执行这种操作的函数被称为高阶函数。回调函数实际上是一种模式。“模式”一词表示解决软件开发中常见问题的某种行之有效的方法。最好将回调函数作为回调模式去使用。

为什么我们需要回调

客户端 JavaScript 在浏览器中运行,并且浏览器的主进程是单线程事件循环。如果我们尝试在单线程事件循环中执行长时间运行的操作,则会阻止该过程。从技术上讲这是不好的,因为过程在等待操作完成时会停止处理其他事件。

例如,alert 语句被视为浏览器中 javascript 中的阻止代码之一。如果运行 alert,则在关闭 alert 对话框窗口之前,你将无法在浏览器中进行任何交互。为了防止阻塞长时间运行的操作,我们使用了回调。

让我们深入研究一下,以便使你准确了解在哪种情况下使用回调。

关于JavaScript回调函数的深入理解

获取并显示消息的函数

在上面的代码片段中,首先执行 getMessage()函数,然后执行 displayMessage() 。两者都在浏览器的控制台窗口中显示了一条消息,并且都立即执行。

在某些情况下,一些代码不会立即执行。例如,如果我们假设 getMessage() 函数执行 API 调用,则必须将请求发送到服务器并等待响应。这时我们应该如何处理呢?

如何使用回调函数

我认为与其告诉你 JavaScript 回调函数的语法,不如在前面的例子中实现回调函数更好。修改后的代码段显示在下面的截图中。

关于JavaScript回调函数的深入理解

用回调函数显示消息

为了使用回调函数,我们需要执行某种无法立即显示结果的任务。为了模拟这种行为,我们用 JavaScript 的 setTimeout() 函数。该函数会暂停两秒钟,然后在控制台窗口中显示消息“ Hi,there”。

“显示的消息”将被显示在浏览器的控制台窗口中。在这种情况下,首先,我们需要等待 getMessage() 函数。成功执行此函数后,再执行 displayMessage() 函数。

回调的工作方式

让我解释一下前面的例子在幕后发生的事。

从上一个例子可以看到,在 getMessage() 函数中,我们传递了两个参数。第一个参数是 msg 变量,该变量显示在浏览器的控制台窗口中,第二个参数是回调函数。

现在,你可能想知道为什么将回调函数作为参数进行传递 —— 要实现回调函数,我们必须将一个函数作为参数传给另一个函数。

在 getMessage() 完成任务后,我们将调用回调函数。之后,当调用 getMessage() 函数时,将引用传给displayMessage() 函数,该函数就是回调函数。

注意,当调用 getMessage() 函数时,我们仅将其引用传给 displayMessage() 函数。这就是为什么你不会在它旁边看到函数调用运算符,也就是() 符号。

Javascript 回调是异步的吗?

JavaScript 被认为是单线程脚本语言。单线程是指 JavaScript 一次执行一个代码块。当 JavaScript 忙于执行一个块时,它不可能移到下一个块。

换句话说,我们可以认为 JavaScript 代码本质上总是阻塞的。但是这种阻塞性使我们无法在某些情况下编写代码,因为在这些情况下我们没有办法在执行某些特定任务后立即得到结果。

我谈论的任务包括以下情况:

  • 通过对某些端点进行 API 调用来获取数据。
  • 通过发送网络请求从远程服务器获取一些资源(例如,文本文件、图像文件、二进制文件等)。

为了处理这些情况,必须编写异步代码,而回调函数是处理这些情况的一种方法。所以从本质上上说,回调函数是异步的。

Javascript 回调地狱

当多个异步函数一个接一个地执行时,会产生回调地狱。它也被称为厄运金字塔。

假设你要获取所有 Github 用户的列表。然后在用户中搜索 JavaScript 库的主要贡献者。再然后,你想要在用户中获取姓名为 John 的人员的详细信息。

为了在回调的帮助下实现这个功能,代码应该如下所示:

http.get('https://api.github.com/users', function(users) {
  /* Display all users */
  console.log(users);
  http.get('https://api.github.com/repos/javascript/contributors?q=contributions&order=desc', function(contributors) {
  /* Display all top contributors */
    console.log(contributors);
    http.get('https://api.github.com/users/Jhon', function(userData) {
    /* Display user with username 'Jhon' */
      console.log(userData);
    });
  });
});

从上面的代码片段中,你可以看到代码变得更加难以理解,以及难以维护和修改。这是由回调函数的嵌套而引发的。

如何避免回调地狱?

可以使用多种技术来避免回调地狱,如下所示。

  1. 使用promise
  2. 借助 async-await
  3. 使用 async.js 库

使用 Async.js 库

让我们谈谈怎样用 async.js 库避免回调地狱。

根据 async.js 官方网站的描述:Async 是一个工具模块,它提供了直接、强大的函数来使用异步 JavaScript。

Async.js 总共提供约 70 个函数。现在,我们将仅讨论其中两个,即 async.waterfall() 和 async.series()。

async.waterfall()

当你要一个接一个地运行某些任务,然后将结果从上一个任务传到下一个任务时,这个函数非常有用。它需要一个函数“任务”数组和一个最终的“回调”函数,它会在“任务”数组中所有的函数完成后,或者用错误对象调用“回调”之后被调用。

var async = require('async');
async.waterfall([
    function(callback) {
      /*  
        Here, the first argument value is null, it indicates that
        the next function will be executed from the array of functions.
        If the value was true or any string then final callback function
        will be executed, other remaining functions in the array 
        will not be executed.
      */
        callback(null, 'one', 'two');
    },
    function(param1, param2, callback) {
        // param1 now equals 'one' and param2 now equals 'two'
        callback(null, 'three');
    },
    function(param1, callback) {
        // param1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    /*
      This is the final callback function.
      result now equals 'done'
    */
});

async.series()

当你要运行一个函数然后在所有函数成功执行后需要获取结果时,它很有用。 async.waterfall() 和 async.series() 之间的主要区别在于, async.series() 不会将数据从一个函数传递到另一个函数。

async.series([
    function(callback) {
        // do some stuff ...
        callback(null, 'one');
    },
    function(callback) {
        // do some more stuff ...
        callback(null, 'two');
    }
],
// optional callback
function(err, results) {
    // results is now equal to ['one', 'two']
});

Javascript 回调与闭包

闭包

用技术术语来说,闭包是捆绑在一起的函数的组合,引用了其周围的状态。

简而言之,闭包允许从内部函数访问外部函数的作用域。

要使用闭包,我们需要在一个函数内部定义另一个函数。然后,我们需要将其返回或传给另一个函数。

回调

从概念上讲,回调类似于闭包。回调基本上是把一个函数作为另一个函数的用法。

最后的话

希望本文能消除你对 javascript 回调函数的所有疑问。如果你觉得这篇文章有帮助,请与他人分享。

到此这篇关于关于JavaScript回调函数的文章就介绍到这了,更多相关JavaScript回调函数内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
一个小型js框架myJSFrame附API使用帮助
Jun 28 Javascript
使用JavaScript检测Firefox浏览器是否启用了Firebug的代码
Dec 28 Javascript
jQuery 瀑布流 浮动布局(一)(延迟AJAX加载图片)
May 23 Javascript
jquery选择器的选择使用及性能介绍
Jan 16 Javascript
JQuery教学之性能优化
May 14 Javascript
JS中产生20位随机数以0-9为例也可以是a-z A-Z
Aug 01 Javascript
JavaScript 学习笔记之操作符
Jan 14 Javascript
AngularJS iframe跨域打开内容时报错误的解决办法
Jan 26 Javascript
百度地图API之百度地图退拽标记点获取经纬度的实现代码
Jan 12 Javascript
mongoose更新对象的两种方法示例比较
Dec 19 Javascript
探索JavaScript中私有成员的相关知识
Jun 13 Javascript
Vue事件修饰符native、self示例详解
Jul 09 Javascript
vue.js Router中嵌套路由的实用示例
Jun 27 #Vue.js
vite+vue3.0+ts+element-plus快速搭建项目的实现
vue-router中hash模式与history模式的区别
Vue-Element-Admin集成自己的接口实现登录跳转
浅谈Web Storage API的使用
Node.js实现断点续传
Jun 23 #Javascript
JavaScript实现登录窗体
You might like
win7计划任务定时执行PHP脚本设置图解
2014/05/09 PHP
PHP APC配置文件2套和参数详解
2014/06/11 PHP
PHP数组函数知识汇总
2016/05/12 PHP
php 使用fopen函数创建、打开文件详解及实例代码
2016/09/24 PHP
javascript new后的constructor属性
2010/08/05 Javascript
jQuery EasyUI API 中文文档 可调整尺寸
2011/09/29 Javascript
JS实现简单的顶部定时关闭层效果
2014/06/15 Javascript
JavaScript中的异常捕捉介绍
2014/12/31 Javascript
jQuery支持添加事件的日历特效代码分享(3种样式)
2015/08/24 Javascript
Node.js Streams文件读写操作详解
2016/07/04 Javascript
select隐藏选中值对应的id,显示其它id的简单实现方法
2016/08/25 Javascript
详解JavaScript中数组的reduce方法
2016/12/02 Javascript
JS表单提交验证、input(type=number) 去三角 刷新验证码
2017/06/21 Javascript
通俗易懂地解释JS中的闭包
2017/10/23 Javascript
使用layui实现树形结构的方法
2019/09/20 Javascript
Python写的Socks5协议代理服务器
2014/08/06 Python
如何用itertools解决无序排列组合的问题
2017/05/18 Python
python matplotlib中文显示参数设置解析
2017/12/15 Python
python Celery定时任务的示例
2018/03/13 Python
浅谈pandas用groupby后对层级索引levels的处理方法
2018/11/06 Python
Python爬虫 bilibili视频弹幕提取过程详解
2019/07/31 Python
Python+appium框架原生代码实现App自动化测试详解
2020/03/06 Python
Python使用Pyqt5实现简易浏览器(最新版本测试过)
2020/04/27 Python
英国最大的奢侈珠宝和手表网站:C W Sellors
2017/02/10 全球购物
德国网上花店:Valentins
2018/08/15 全球购物
莫斯科隐形眼镜网上商店:Linzi
2019/07/22 全球购物
文秘专业毕业生就业推荐信
2013/11/08 职场文书
2014年公司植树节活动方案
2014/03/04 职场文书
2014年世界艾滋病日演讲稿
2014/11/28 职场文书
初中英语教师个人工作总结
2015/02/09 职场文书
小学教师师德师风承诺书
2015/04/28 职场文书
工资证明范本
2015/06/12 职场文书
民主生活会主持词
2015/07/01 职场文书
2015大学迎新晚会策划书
2015/07/16 职场文书
小学教师师德培训心得体会
2016/01/09 职场文书
java设计模式--七大原则详解
2021/07/21 Java/Android