当前位置: 首页 > news >正文

JavaScript 中的异步编程(上)

JavaScript 异步编程都有哪些方案?

  • 回调函数、事件监听,PromiseGeneratorasync/await,这几种 JS 的编码方式都是异步编程。

一、什么是同步?

所谓同步就是在执行某段代码时,在该代码没有得到返回结果之前,其他代码暂时是无法执行的,但是一旦执行完成拿到返回值之后,就可以执行其他代码了。换句话说,在此代码执行完未返回结果之前,会阻塞之后的代码执行,这样的情况称为同步。

二、什么是异步?

某一段代码执行异步过程调用发出后,这段代码不会立即得到返回结果。而是在异步调用发出之后,一般通过回调函数处理这个调用之后拿到结果。异步调用发出后,不会影响阻塞后面的代码执行,这样的情况称为异步。

三、JS 编程中为什么需要异步?

JavaScript 是单线程的,如果 JS 都是同步代码执行意味着,专业会造成阻塞,如果当前我们有一段代码需要执行时,如果使用同步的方式,那么就会阻塞后面的代码执行;而如果使用异步则不会阻塞,我们不需要等待异步代码执行的返回结果,可以继续执行该异步任务之后的代码逻辑。 因此,JavaScript中会使用大量异步进行编程。

四、JavaScript 异步编程方式发展历程

1.回调函数

早些年为了实现 JS 的异步编程,一般都采用回调函数的方式,比如比较典型的事件回调,或者使用 setTimeout/setInterval 来实现一些异步编程的操作,但是使用回调函数来实现存在一个很常见的问题,那就是回调地狱。

fs.readFile(A, 'utf-8', function (err, data) {fs.readFile(B, 'utf-8', function (err, data) {fs.readFile(C, 'utf-8', function (err, data) {fs.readFile(D, 'utf-8', function (err, data) {// ......})})})
})
/* 先读取A文本内存,再根据A文本内容读取B,然后再根据B的内容读取C。 */ 

回调实现异步编程的场景也有很多,比如:

1.ajax 请求的回调
2.定时器中的回调
3.事件回调
4.Nodejs 中的一些方法回调

  • 异步回调如果层级很少,可读性和代码的维护性暂时还是可以接受, 但一旦层级变多就会陷入回调地狱,上面这些异步编程的场景都会涉及回调地狱的问题,下面我们来看一下针对上面这个业务场景,改成 Promise 来实现异步编程,会是什么样子呢???

2.Promise

采用 Promise 的实现方式在一定程度上解决了回调地狱的问题

function read(url) {return new Promise((resolve, reject) => {fs.readFile(url, 'utf-8', (err, data) => {if (err) reject(err)resolve(data)})})
}
read(A).then((data) => {return read(B)}).then((data) => {return read(C)}).then((data) => {return read(D)}).catch((res) => {console.log(res)})
/* 可读性的确有一定的提升,优点是可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,
但是 Promise 也存在一些问题,即便是使用 Promise 的链式调用,
如果操作过多,其实并没有从根本上解决回调地狱的问题,只是换了一种写法,
可读性虽然有所提升,但是依旧很难维护。 */
/* 不过 Promise 又提供了一个 all 方法,对应这个业务场景的代码,用 all 来实现可能效果会更好 */

function read(url) {return new Promise((resolve, reject) => {fs.readFile(url, 'utf-8', (err, data) => {if (err) reject(err)resolve(data)})})
}
// 通过 Promise.all 可以实现多个异步并行执行,统一时刻获取最终结果的问题
Promise.all([read(A), read(B), read(C)]).then((data) => {console.log(data)}).catch((err) => {console.log(err)}) 

3.Generator

也是一种异步编程解决方案,它最大的特点是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都有 yield 语法来标注。Generator 函数一般配合 yield 使用,Generator 函数最后返回的是迭代器。

function* gen() {let a = yield 111console.log(a)let b = yield 222console.log(b)let c = yield 333console.log(c)let d = yield 444console.log(d)
}
let t = gen()
t.next(1) // 第一次调用 next 函数时,传递的参数无效,故无打印结果
t.next(2) // a 输出 2
t.next(3) // b 输出 3
t.next(4) // c 输出 4
t.next(5) // d 输出 5
/* 第一次的 next 虽然执行了但是并未输出结果,
后面的每次执行 next 会把参数传入然后打印出来,
等到最后一次 next 对应的 yield 执行完之后,
控制台会打印 "{value:undefined,done:true}" 的输出结果,标识该 Generator 函数已经执行完毕,即 done:true */ 

4.async/await

ES7 中提出的新的异步解决方案,async 是 Generator 函数的语法糖,async/await 的优点是代码清晰(不像使用 Promise 的时候需要写很多 then 的方法链),可以处理回调地狱对的问题。async/await 写起来使得 JS 的异步代码看起来像同步代码,其实异步编程发展的目标就是让异步逻辑的代码看起来像同步一样容易理解

function testWait() {return new Promise((resolve, reject) => {setTimeout(() => {console.log('testWait')resolve()}, 1000)})
}
async function testWaitUse() {await testWait()console.log('hello')return 123// 输出顺序:testWait,hello// 第十行如果不使用 await 输出顺序:hello,testWait
}
console.log(testWaitUse())

/* 在正常的执行顺序下,testWait 这个函数由于使用的是 setTimeout 的定时器,
回调会在一秒之后执行,但是由于执行到这里采用了 await 关键词,
testAwaitUse 函数在执行的过程中需要等待 testWait 函数执行完成之后,
再执行打印 hello 的操作。 */
/* 但是如果去掉 await,打印结果的顺序就会变化 */
// async/await 不仅仅是 JS 的异步编程的一种方式,其可读性也接近于同步代码,让人更容易理解 

五、总结

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

相关文章:

  • 【一起学数据结构与算法】快速教你了解并实现单链表
  • 用Pytorch实现一个线性回归
  • 【C++】二叉搜索树set/map
  • 最短路径查找Dijkstra算法
  • [数字媒体] Photoshop基础之图像校正、抠图(证件照)和融合
  • 【毕业设计】基于的单片机的移动硬盘设计与实现 - stm32 嵌入式 物联网
  • 使用Python的requests库发送SOAP请求,错误码415
  • Python爬虫技术系列-02HTML解析-lxml+BS4
  • 今日头条——机器学习算法岗1234面
  • 【笔记】快速理解傅里叶级数
  • 宣布发布 .NET 7 Release Candidate 1
  • 8万Star,这个开源项目有点强
  • 数据批处理速度慢?不妨试试这个
  • 透过安全事件剖析黑客组织攻击技术(2FA/MA的攻击手法)
  • java毕业设计——基于Java+AI的五子棋游戏设计与实现(毕业论文+程序源码)——五子棋游戏
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • Angular Elements 及其运作原理
  • CentOS 7 防火墙操作
  • JavaScript 奇技淫巧
  • Java的Interrupt与线程中断
  • java小心机(3)| 浅析finalize()
  • tab.js分享及浏览器兼容性问题汇总
  • ubuntu 下nginx安装 并支持https协议
  • vuex 笔记整理
  • windows下使用nginx调试简介
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 阿里云应用高可用服务公测发布
  • 码农张的Bug人生 - 初来乍到
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 深度解析利用ES6进行Promise封装总结
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 使用权重正则化较少模型过拟合
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 问题之ssh中Host key verification failed的解决
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 译有关态射的一切
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • !$boo在php中什么意思,php前戏
  • #162 (Div. 2)
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • ***原理与防范
  • .form文件_SSM框架文件上传篇
  • .NET CLR基本术语
  • .NET Core 成都线下面基会拉开序幕
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。