当前位置: 首页 > 编程日记 > 正文

JS异步编程之callback

为什么 JS 是单线程?

众所周知,Javascript 语言的执行环境是"单线程"(single thread)。

所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

而浏览器是多线程的,JS 线程就是其中一个:

  • 浏览器 GUI 渲染线程
  • JavaScript 引擎线程
  • 浏览器定时触发器线程
  • 浏览器事件触发线程
  • 浏览器 http 异步请求线程

浏览器线程知识中重要的一点是:

GUI渲染进程和 JavaScript 引擎进程是互斥的,因为如果这两个线程可以同时运行的话, JavaScript 的 DOM 操作将会扰乱渲染线程执行渲染前后的数据一致性。而且如果 DOM 一变化,界面就立刻重新渲染,效率必然很低

所以 JS 主线程执行任务时,浏览器渲染线程处于挂起状态。

同理,如果 JS 采用多线程同步的模型,那么如何保证同一时间修改了 DOM, 到底是哪个线程先生效呢?从操作系统调度多线程的上下文开销,到实际编程里的锁、线程同步等问题,都让开发变得比较困难。

所以 JS 最终采用了单线程的事件模型。

我之前的文章《JS专题之事件循环》也有讲过这块内容,欢迎翻阅。

一、同步与异步

单线程模式这种排队执行的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

那同步和异步的区别是什么?

我们想象一个很常见的场景:我们去面馆吃牛肉面,柜台人很多,前面在排队下单。

这个时候,同步就是,收银员收了你的钱,告诉你要在柜台站着等面煮好,煮好后,就端面开吃,后面的人也只能等前面的人面煮好了才能付款下单然后等着面煮好端走~

而异步就是,收银员收了你的钱,然后给了你一张小票,小票上有一个你的编号,收银员告诉你,可以去座位上,你的面一煮好,会大声叫你,你就来端面开吃。

我们可以看出,我们是过程的调用者,面馆是被调用者,牛肉面煮好,是我们想要的结果,同步是调用者需要主动地等待这个结果。异步是被动的等待结果,当被调用者有结果了,就会通过消息机制或者回调机制告诉调用者结果。

同步和异步关注的是消息通信机制,同步就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。

而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果, 而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

以上:

  • 下单吃面是发起调用函数
  • 端面开吃的回调函数
  • 煮好的面是调用的结果,也是回调函数的参数

将例子抽象成伪代码:

orderNoodle("牛肉面", function(noodle) {// 端面getNoodle();// 吃面eatNoodle();
});
复制代码

三、事件循环

关于事件循环如何执行异步代码可以翻阅前面的文章《JS专题之事件循环》,这里大概提一下。

如果遇到异步事件,JS 引擎会把事件函数压入执行调用栈,但浏览器识别到它是异步事件后,会将其弹出执行栈,当异步函数有返回结果后,JS 引擎将异步事件的回调函数放入事件队列中,如果执行调用栈为空,就将回调函数压入执行调用栈执行。

四、回调函数

在 JavaScript 中,函数 function 作为一等公民,使用上非常自由,无论调用它,或者作为参数,或者作为返回值都可以。

因为单线程异步的特点,后来在 JS 中,慢慢将函数的业务重点转移到了回调函数中。

function step1(cb) {console.log("step1");cb()
}function step2(){console.log("step2");
}step1(step2);  // step1  step2
复制代码

代码会按先后顺序执行 step1, step2。

现在假设我们有这样的需求:请求文件1后,获取文件1 中的数据后请求文件2,获取文件 2 中的数据后,又请求文件三。

var fs = require("fs");fs.readFile("./file1.json", function(err, data1) {fs.readFile("./file2.json", function (err, data2) {fs.readFile("./file3.json", function(err, data3) {})})
})
复制代码

五、回调函数的问题

由第四节可以看出,回调函数的写法存在很多问题。

  1. 回调地狱(洋葱模型)

当多个异步事务多级依赖时,回调函数会形成多级的嵌套,被花括号一层层包括,代码变成 金字塔型结构,也被称为回调地狱和洋葱模型。

在回调地狱的情况下,代码逻辑的梳理,流程的控制,代码封装维护,错误处理都变得越来越困难。

  1. 异常处理

try...catch 是被设计成捕获当前执行环境的异常,意思是只能捕获同步代码里面的异常,异步调用里面的异常无法捕获。

function readFile(fileName) {setTimeout(function () {throw new Error("类型错误");}, 1000);
}
try {readFile('./file1.json');
} catch (e) {// 如果异步事件出错,打印不出来错误信息console.log('err', e);
}
复制代码

在 nodejs 对回调函数采用 error first 的思想,回调函数的第一个参数保留给一个错误error对象,如果有错误发生,错误将通过第一个参数err返回。

原因是一个有回调函数的函数,执行分两段,第一段执行完之后,任务所在的上下文环境就已经结束了。在这以后抛出的错误,原来的上下文已经无法捕捉,只能当做参数,传入第二阶段。

fs.readFile('/etc/passwd', 'utf8', function (err, data) {if(err) {console.log(err)return;}
});
复制代码

总结

回调函数是 JS 异步编程中的基石,但同时也存在很多问题,不太适合人类自然语言的线性思维习惯。

接下来几篇文章,我将梳理 JS 中异步编程中的历史演进中 Promise, generator, async&await 相关的内容,欢迎关注。

欢迎关注我的个人公众号“谢南波”,专注分享原创文章。

掘金专栏 JavaScript 系列文章

  1. JavaScript之变量及作用域
  2. JavaScript之声明提升
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之作用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中彻底理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
  14. JavaScript专题之模拟实现new
  15. JS专题之事件模型
  16. JS专题之事件循环
  17. JS专题之去抖函数
  18. JS专题之节流函数
  19. JS专题之函数柯里化
  20. JS专题之数组去重
  21. JS专题之深浅拷贝
  22. JS专题之数组展开
  23. JS专题之严格模式
  24. JS专题之memoization
  25. JS专题之垃圾回收
  26. JS专题之继承

相关文章:

黑盒测试之两两组合方法

两两组合方法 概念需求示例测试用例分析设计总结概念 所有测试数据两两配对,每一对数据至少出现一次,这个是两两组合测试的基本原理,两两组合测试也称结对测试(Pairwise Testing)。大部分缺陷是在进行两个变量取值冲突的测试时被发现的,不仅仅是在所有的组合情况下才会被…

用JavaScript获取一个超链接的绝对URL地址

对于Web程序员来说,处理简单的URL格式也许会成为一场噩梦。试想一下,一个网址里有很多组成部分都会影响你对它的解析方法: 是否以/字符开头是否以//开头是否以?号开头是否以#号开头…等等当你想要这个地址的绝对地址时,如何判断处…

Docker学习笔记_安装ActiveMQ

一、实验环境 1、宿主机OS:Win10 64位 2、虚拟机OS:Ubuntu18.04,虚拟机名称:Ubuntu18VM1,虚拟机IP:192.168.8.25 3、操作账号 :Docker 4、在虚拟机上已安装Docker 二、安装 简要步骤: 1.搜索镜像 sudo …

Ubuntu下配置Nginx HTTPS

HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:…

因0x764fb11c的错误状态_《最强大脑》国际赛王易木又被质疑作弊,因背反答案露出了马脚?...

《最强大脑之燃烧吧大脑》第二季国际赛最后一场,中国战队和国际战队在3V3的团战当中以绝对优胜的姿态拿下了本场比赛。在观众为郑林楷、宋一骜以及王易木的成功感到高兴之际,有部分吃瓜群众跳出来指出,之前因作弊而饱受质疑的王易木此次又有了…

[PyQt4]项目开发中遇到的错误与解决办法

1假如将ui文件py化以后产生的关于界面的类是继承object的ui_dialog,方法是setupui,则在主程序中应: app QtGui.QApplication(sys.argv)dialogQtGui.QWidget()ud Ui_Dialog()ud.setupUi(dialog)dialog.show()sys.exit(app.exec_()) 2自建槽checklog_slo…

判断出栈顺序是否正确(栈的压入、弹出序列)

输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。 比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。因为…

LeetCode-135-Candy

算法描述: There are N children standing in a line. Each child is assigned a rating value. You are giving candies to these children subjected to the following requirements: Each child must have at least one candy.Children with a higher rating get…

消控中心人员配置_物业公司(项目)各岗位员工配置人数及标准(供参考)

一物业定编的目的1、优化编制结构,加强编制规模的合理化, 满足项目物业系统的人员需求;2、通过编制管理逐步,提升各大区住宅物业服务中心人均管理面积;3、通过编制管理达到物业系统整体扭亏的目标。二物业定编的能效目标1、费用控…

weblogic域,管理服务器,受管服务器,集群和机器的基本知识

1.域(Domain) •它是什么?–是一个逻辑上管理的WebLogic Server组,这些组从管理上当作一个整体来操作•域里面有什么?–服务器–服务器集群–机器•规则:–同一个域中的所有WebLogic服务器实例必须处于同样的版本。–域中的服务器…

软件版本号讲解

1. 软件版本阶段说明* Alpha版: 此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。* Beta版: 该版本相对于α版已有了很大的改进,消除了严重…

vscode用鼠标滚轮_「鼠标」垂直鼠标体验之三 麦塔奇Rockstick2摇杆鼠标

Microtouch麦塔奇是我体验的第三个垂直鼠标品牌了,前两个分别是罗技和多彩。对比来看,这次上手的Rockstick2是我目前体验的垂直鼠标里最迷你的一个。整体外观先上图给大家看一下~到手了才发现这个鼠标其实分了两种大小,一个是small/medium&am…

Go 领军人物谢孟军:智能制造渴望银弹,首先要摒弃偏见

2019 年 1 月 13 日,TGO 鲲鹏会厦门分会正式成立,数百名技术人赶赴现场为庆祝 TGO 鲲鹏会厦门分会的诞生。本篇文章根据谢孟军在活动现场分享的「基于工业大数据下的工厂系统实践」整理,有部分不改变原意的删减。谢孟军现场演讲照片今天&…

Git与Svn的区别—笔记1

1. Git是分布式版本控制系统,不需要联网就可以使用,且速度较快;而Svn是集中式的版本控制系统,必须联网才能使用,且速度较慢 2. Git把内容按元数据方式存储,而Svn是按文件存储,Git的目录是处于你…

电商平台战 运营是关键

9月19日,淘宝商城宣布推出开放的B2C平台战略,与包括一号店、银泰网、库巴网、易讯等在内的38家国内垂直B2C企业联合宣布达成战略合作,38家垂直B2C企业将集体在淘宝商城开设官方旗舰店。 据淘宝商城总裁张勇表示,淘宝商城的战略是要…

三菱fx2n64mr说明书_三菱FX2N可编程控制器使用手册

三菱FX2N可编程控制器使用手册一、可编程控制器的内部编程元件1、输入继电器X:X000~X017共16点2、输出继电器Y:Y000~Y017共16点3、辅助继电器M:1)通用辅助继电器M0~M499共500点2)断电保持继电器M500&#x…

Android开发学习笔记:WebView 一

WebView(网络视图)能加载显示网页,可以将其视为一个浏览器。它使用了WebKit渲染引擎加载显示网页,实现WebView有以下两种不同的方法: 第一种方法的步骤: 1.在要Activity中实例化WebView组件:WebView webView new WebV…

Linux Redis 高可用之主从复制

Redis主从复制简介 和MySQL主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以…

别人总结的批处理技巧

1、不显示代码执行期间的各种提示信息   很多时候,为了使得屏幕上不出现无关的信息,我们需要屏蔽掉命令执行过程中产生的一些提示,有时候是屏蔽掉出错信息,有时候是为了屏蔽成功执行命令的信息,有时候两者都需要屏蔽…

h3c交换机端口加入vlan命令_华为交换机批量加入 Vlan 方法

华为交换机单独加入vlan太麻烦,思科交换机有批量加入vlan的方法,当然华为也有。要求 1~6口划分到vlan2,6~12口划分到vlan3,13~18口划分到vlan4,19~24口划分到vlan5.25,~26 加入trunk. 1、创建vlan 2~vlan4,4个vlan 创建vlan 2~vlan4,4个vlan 也可以使用命令 batch 2 to 5 2、…

我是如何设计 Upload 上传组件的

Upload 组件设计的目标是解决用户上传文件的便利性&#xff0c;但是中后台 Upload 组件的场景是多种多样的&#xff0c;所以可扩展能力是 Upload 组件不可忽视的另一方面。 同样为了大家能够更加容易的理解&#xff0c;我会从最原始的 input 标签开始说起 <form action"…

设计模式之资料摘录

本文主要是摘录了网上的一些资料&#xff0c;目前在慢慢学习这些东西&#xff0c;还谈不上个人的理解&#xff0c;离在项目中的应用也很遥远&#xff0c;后期会补上个人理解与实际项目中的应用 总体来说设计模式分为三大类&#xff1a; 创建型模式&#xff0c;共五种&#xff1…

Web.Config文件配置之连接默认错误页

在一些网站中&#xff0c;当网络地址发生错误时&#xff0c;通常会自动跳转到一个页面&#xff0c;并在该页面显示错误信息&#xff0c;此功能叶可以通过配置Web.Config文件配置实现。例如访问者在访问网站时出现错误&#xff0c;程序将跳转到默认页面error.aspx。在Web.Config…

新版rust怎么拆除建筑_市政工程造价怎么学 龙岗园林市政造价课程

课程对象&#xff1a;1、建筑相关专业或者非建筑专业应届毕业生&#xff1b;2、从事施工、资料、安全、招标等岗位&#xff0c;欲转岗做造价工作的人员&#xff1b;3、企业新入职工程造价人员&#xff1b;4、其他专业/行业&#xff0c;欲转行做造价的人员。5、已经在造价行业从…

HDFS Federation与HDFS High Availability详解

HDFS Federation    NameNode在内存中保存文件系统中每个文件和每个数据块的引用关系&#xff0c;这意味着对于一个拥有大量文件的超大集群来说&#xff0c;内存将成为限制系统横向扩展的瓶颈。在2.0发行版本系列中引入的Federation HDFS允许  系统通过添加NameNode实现扩…

win32的一个售票程序,收获有非常的多

先秀一下我的收获吧&#xff01; 1、在创建非模态对话框的时&#xff0c;需要用到createdialog函数&#xff0c;第二个参数需要注意是填写对话框的资源标识符&#xff08;id&#xff09;&#xff0c;之后需要调用showwindow来显示对话框&#xff1b;或者不这么做也行&#xff0…

从理论到实践 全面理解HTTP/2

前言 为了降低加载时间&#xff0c;相信大多数人都做过如下尝试 Keep-alive: TCP持久连接&#xff0c;增加了TCP连接的复用性&#xff0c;但只有当上一个请求/响应完全完成后&#xff0c;client才能发送下一个请求Pipelining: 可同时发送多个请求&#xff0c;但是服务器必须严格…

开机不进去桌面执行gui_电脑系统崩溃进不去,简单几步设置U盘,快速找回桌面重要文件...

hello~我可爱的粉丝们&#xff01;今天粮小白分享一个电脑系统崩溃无法开机&#xff0c;怎么把电脑数据保存备份的小技能&#xff0c;就来就点个关注吧。每天更新生活必备技能哦。电脑系统崩溃进不去&#xff0c;简单几步设置U盘&#xff0c;快速找回桌面重要文件近些年来&…

java遍历给定目录,树形结构输出所有文件,包括子目录中的文件

http://www.cnblogs.com/jenson138/p/4691418.html import java.io.File;public class ReadDirectory {// 文件所在的层数private int fileLevel;/*** 生成输出格式* param name 输出的文件名或目录名* param level 输出的文件名或者目录名所在的层次* return 输出的字符串*/pu…

数据千万条,备份第一条,数据找不回,老板两行泪

2019独角兽企业重金招聘Python工程师标准>>> 开工第一天&#xff0c;请带着你的回忆看下文&#xff0c;想想你这些年删过的库&#xff0c;被删过的库。。。 数据库备份是个老生常谈的话题&#xff0c;看似很简单&#xff0c;但在实际操作过程中&#xff0c;运维人员…