[小白教程] Javascript Callback以及Promise/async/await 一文通

news2024/11/8 22:42:19

在这里插入图片描述

一、最初

一切从 Javascript 是一门异步编程语言说起,比如这种最简单的:

  let n = 0
  function f1() {
    setTimeout(function () {
      n++
    }, 1000)
  }

  f1()
  console.log(n)

可能直觉上会觉得最终n=1,但实际上打印出来的是0,因为尽管调用了f1函数,但主进程并不会等待其内部的定时器到时之后再继续往下执行,而是直接就console.log(n)了,而此时n=0。


二、Callback (回调函数) 方案

为了解决这个问题,人们想出了回调函数,也就是说当一个函数需要一定的时间去运行,那么就等它执行完毕之后再反向调用外部的某个函数,将上例改写如下:

  let n = 0
  function f1(function_name) {
    setTimeout(() => {
      n++
      function_name(n);
    }, 1000);
  }

  function f2 (ret) {
    console.log(ret)
  }

  f1(f2)

这次打印出来就是n=1了,f1函数多了一个参数function_name,这个参数是告诉f1,当函数运行完毕后,将返回值通过function_name这个函数携带出来,这是一种反向调用,也就是所谓的 “回调函数 (callback function)”。

  • 匿名函数
    在调用回调函数的时候,可以不指定函数名,将函数体直接嵌到形参里面,还是上面那个例子,可以写成这样:
  let n = 0
  function f1(function_name) {
    setTimeout(() => {
      n++
      function_name(n);
    }, 1000);
  }

  f1(function (ret) { 
    console.log(ret) 
  })

这种写法并不难理解,就是将函数f2直接嵌入了f1的参数列表里。


三、回调地狱

尽管回调函数解决了异步执行返回值的确定性问题,但是如果程序逻辑需要进行多重异步操作,就会导致经典的 “回调地狱”,比如这样:

  let n = 0
  function f1(function_name) {
    setTimeout(() => {
      n++
      function_name(n);
    }, 1000);
  }

  const f3 = f2 = f1

  f1(function (ret) {
    f2(function (ret) {
      f3(function (ret) {
        console.log(ret)
      })
    })
  })

最终结果是n=3,以上演示只写了三层,实际上超过3层的逻辑在实际生活中是很常见的,回调地狱将会让代码的可读性降低,复杂性大大增加。


四、Promise 方案

Promise是ES6中的新特性,其最主要是目的就是要解决“老式”的回调函数所产生的“地狱”问题。将上例改写如下:

  let n = 0
  function f1() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        n++
        resolve(n)
      }, 1000);
    })
  }

  const f3 = f2 = f1

  f1()
    .then(() => f2())
    .then(() => f3())
    .then(
      ret => {
        console.log(ret)
      })

最终结果和回调版本是一样的,依然是 n=3 ,但书写和理解性就比前者容易多了,通过.then()来形成所谓的 “链式调用“,即在上一个操作执行成功之后,开始下一个的操作。

使用上需要注意的有两点,首先用 return new Promise((resolve, reject) => { ... }) 将耗时操作包裹起来,而 resolve (ret) 的意义则是将返回值携带出来,类似 return (ret)

PS:上例中没有用到第二个参数 reject ,通过它将抛出一个拒绝执行的信息,比如我们将上例的resolve(n) 改为 reject(n) ,链式调用将会中断, f2、f3将不会被执行。reject 一般与catch 错误捕捉机制协同使用,当然如果没有这个需求的话,reject 可以省略。

  • 箭头函数
    上例中采用了很多 => 这样的 ”箭头“ 函数,对应老式的function函数声明方式,以下是对照:

  • 带名字的函数
    不带参数: abc = () => { 函数体 } 等于 function abc() { 函数体 }
    带参数: abc = (v1, v2) => { 函数体 } 等于 function abc(v1, v2) { 函数体 }

  • 匿名函数
    不带参数: () => { 函数体 } 等于 function () { 函数体 }
    带参数: (v1, v2) => { 函数体 } 等于 function (v1, v2) { 函数体 }
    PS: 如果只有一个变量,可以不带括号,比如:

  abc = v1 => {
    console.log(v1)
  }

或者如上例的:

  ret => {
    console.log(ret)
  }

五、async 与 await

通过async 和 await,可以写出更加像 “同步式” 编程语言那样的代码,例如:

  let n = 0
  function f1() {
    return new Promise((resolve) => {
      setTimeout(() => {
        n++
        resolve(n)
      }, 1000);
    })
  }

  const f3 = f2 = f1

  async function run() {
    await f1()
    console.log(n)
    await f2()
    console.log(n)
    await f3()
    console.log(n)
  }

  run()

注意,async/wait 这种方案不能直接在主程序的最外层调用,而是必须指定一个 “执行函数”, 并在它前面添加一个 async 关键字,本例中是 async function run() ,而在这个执行函数体内,需要调用的函数前面加一个await关键字,表示将同步执行该函数,上例中f1、f2、f3前面都添加了await,则表示这三个函数是一个挨着一个按顺序执行的。想象一下,在最初的Javascript例子中,这段代码一定是一次性输出3个0,而不是像现在这样间隔1秒输出:1、2、3。这种体验其实跟同步式编程语言已经非常接近了。


后记

本文对Javascript的异步执行机制,回调函数,Promise机制以及箭头函数等内容只讲了一点皮毛,纯属抛砖引玉。本文的目的也并非深入讨论这些知识点,而是想让完全不熟悉的同学能尽快先 “体验” 一下,并对这些内容产生一些感性认识,需要深入学习的同学可以参考以下链接:

Promise:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arrow_functions

https://juejin.cn/post/7108187709076111367

https://juejin.cn/post/7235177983312216125

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/606132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue笔记——实现打印功能1

第一步:安装vue-print-nb,打开项目终端输入 npm install vue-print-nb --save 第二步:打开package.json文件,在dependencies中出现vue-print-nb,说明安装成功,如下图所示。 第三步: 方法一:全…

电子科技大学计算机系统结构复习笔记(二):指令系统

目录 前言 重点一览 指令集系统结构(ISA)的分类 分类依据 存储结构 区别 图示 通用寄存器系统结构分类 存储器寻址 概述 寻址方式 MIPS寻址模式 小结 操作数类型 指令操作 与指令编码 常用操作数类型 常用指令系统的操作 常用指令系统编…

【Pytorch基础教程40】DLRM推荐算法模型部署

note 文章目录 note一、DLRM模型1. 特征工程和embedding层2. butterfly shuffle3. 模型结构 二、模型部署Reference 一、DLRM模型 DLRM是2020年meta提出的工业界推荐算法模型,模型结构非常简单,也没用到什么attention机制等的东西,更多是注重…

权限维持-关于影子用户

前言 影子用户相信大家都是比较熟悉,是一种权限维持的好方法。 注:单机和域环境都可以使用,但是域中可能没有那么好用。 复现 一.本地 1. 正常的影子用户 我们在cmd命令中在生成用户时,在用户名后面加上$就可以 net user test$ …

【cfeng work】什么是SaaS? SaaS详细介绍

WorkProj 内容管理 SaaSSaaS的优势SaaS的注意项SaaS产品核心组件cfeng结合work理解SaaS 本文introduce SaaS的相关内容 昨天cfeng已经介绍过云原生了,其实就是应用在设计上就要围绕Cloud,代表技术就是容器化和微服务、DevOps和区别于传统瀑布模型的持续更…

【Linux】——多线程

目录 Linux线程概念 二级页表 线程的优点 线程的缺点 线程异常 线程的用途 Linux中的线程和进程 进程和线程 进程的多个线程共享 进程和线程的关系 Linux线程控制 POSIX线程库 线程创建 线程ID及地址空间布局 线程等待 线程终止 Linux线程概念 什…

华为OD机试真题 Java 实现【找终点】【2023 B卷 100分】,附详细解题思路

一、题目描述 给定一个正整数数组&#xff0c;设为nums&#xff0c;最大为100个成员&#xff0c;求从第一个成员开始&#xff0c;正好走到数组最后一个成员&#xff0c;所使用的最少步骤数。 要求&#xff1a; 第一步必须从第一元素开始&#xff0c;且1 < 第一步的步长 &…

初出茅庐的小李博客之CAN通信基础知识

CAN是什么&#xff1f; CAN 是 Controller Area Network 的缩写&#xff0c;中文是控制器局域网路,是 ISO 国际标准化的串行通信协议之一。 CAN:控制器局域网( Controller Area Network)属于现场总线的范畴&#xff0c;是一种有效支持分布式控制系统的串行通信网络 CAN是由德…

MySQL Windows 64位解压版安装

1、下载MySQL安装包 下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 选择安装版本&#xff1a;我选择的是5.1.43的版本&#xff0c;下载到本地&#xff0c;并解压到自己想要放的位置&#xff0c;比如&#xff1a;D:\soft 2、在D:\soft\m…

S7-200 PLC新特性是什么

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲(课程筹备中) 西门子200PLC能做什么&#xff1f; 它可以控制设备&#xff0c;实现自动控制&#xff0c;比如切割机&#xff0c;木雕刻机&#xff0c;写字机&#xff0c;锅炉的自动控制等等&#xf…

vscode工具使用

语言的使用&#xff1a; 安装chinse Lagnuage插件 关于设置 (1) 通过可视化界面的方式设置&#xff0c;File -> Preferences -> Settings 快捷键&#xff1a;ctrl , (<)打开 ctrl F4 关闭 (2) 通过json配置文件方式设置&#xff0c;ctrlshiftp, 选择Open …

c++继承详解

前言 继承是类复用的重要方式&#xff0c;学习面向对象语言时学习继承是必不可少的&#xff0c;在c中继承机制一种较为复杂的机制&#xff0c;下面让我们一起来认识一下c中的继承。 目录 1.继承的概念和定义 1.1继承的概念 1.2 继承的定义 2.基类和派生类之间的转换 3.继承…

基于html+css的图展示106

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

PCL点云处理之分层切片法计算树冠投影面积 (一百七十四)

PCL点云处理之分层切片法计算树冠投影面积 (一百七十四) 一、算法介绍二、方法流程三、具体实验1.代码2.效果四、算法总结一、算法介绍 在上一节中,通过树冠整体投影到同一水平面后,计算凸包面积,粗略估计了树冠投影面积,但在通常的研究学习中,这种方法较为笼统,大部分…

springboot配置使用redis序列化时报错“无法自动装配。找不到 ‘RedisConnectionFactory‘ 类型的 Bean”

今天在使用springboot操作redis时出现乱码的问题 像这样&#xff0c;这里对应的key明实际上时springboot:string 对应的值时徐浩的redis 但是当向redis推送数据时就乱码了&#xff0c;后面一查是因为在springboot-redis中&#xff0c;默认配置没有序列化&#xff0c;直接将str…

Python词云绘制

Python词云绘制 效果展示以及准备工作&#xff1a;进入代码书写 效果展示以及准备工作&#xff1a; 效果展示图&#xff1a; 准备工作 pycharm安装第三方库numpy,jieba,wordcloud词云文本的准备&#xff08;.txt&#xff09;背景图的准备&#xff08;我是用的PS&#xff09;…

[激光原理与应用-69]:激光焊接的10大常见缺陷及解决方法

激光焊接是一种以高能量密度的激光束作为热源的高效精密焊接方法。如今&#xff0c;激光焊接已广泛应用于各个行业&#xff0c;如&#xff1a;电子零件、汽车制造、航空航天等工业制造领域。但是&#xff0c;在激光焊接的过程中&#xff0c;难免会出现一些缺陷或次品。只有充分…

[架构之路-203] - 对系统需求类型的进一步澄清

目录 业务/商业需求&#xff1a; 用户/客户需求&#xff1a; 功能性需求&#xff1a; 非功能性需求&#xff1a; 系统需求&#xff1a; 约束条件&#xff1a; 软件需求说明书&#xff1a; 软件质量&#xff1a; 业务/商业需求&#xff1a; 是自顶向下的需求&#xff0…

pytorch卷积神经网络CNN 手写数字识别 MNIST数据集

模型结构和训练代码来自这里 https://blog.csdn.net/weixin_41477928/article/details/123385000 俺又加了离线测试的代码: 第一次运行此代码&#xff0c;需有网络&#xff0c;会下载开源数据集MNIST训练的过程中会把10个epoch的模型均保存到./models下&#xff0c;可能需要你…

2023年第三届陕西省大学生网络安全技能大赛--本科高校组 Reverse题解

文章目录 一. 我的upx -d怎么坏了1. 查看节区信息2. 动态调试脱壳3.输出迷宫图4.走迷宫 二. babypython1.字节码简单分析2. gpt分析3. 程序逻辑4.解题脚本 三. BadCoffee1. 相关文章2.解混淆3.解题脚本 四. Web&Assembly(暂时没复现出来,提供一些相关文章)总结 这次比赛做出…