JavaScript 异步编程(一):认识异步编程
前言
“异步”的大规模流行是在 Web 2.0浪潮中,它伴随着 AJAX 席卷了 Web。前端充斥了各种 AJAX 和事件,这些都是典型的异步应用场景。现在的 Web 应用已经不再是单台服务器就能胜任的时代了,在跨网络的架构下,异步已经是必不可少的标准配备了。
在浏览器中 JS 在单线程上执行,而它还与 UI 渲染共用一个线程。这意味着 JS 在执行的时候,UI 渲染和响应是处于停滞状态的。如果网页临时需要获取一个网络资源,通过同步的方式获取,JS 需要等待资源完成从服务器端获取后才能继续执行,这期间 UI 将停顿,不响应用户的交互行为。而采用异步请求,在请求资源期间,JS 和 UI 的执行都不会处于等待状态,可以继续响应用户的交互行为,给用户一个鲜活的页面。
何为异步
任务队列
对于长时间响应的任务,可以把它们放进另一个队列中,不影响其他任务的执行,这个队列就是任务队列。
存在于任务队列中的任务,又叫作异步任务。异步任务区别于同步任务,同步任务是在主线程中执行的,它通常来说不会消耗太多的时间去执行,所以放在主线程中非常合适。
事件循环
任务队列中的任务(异步任务)需要使用事件循环机制来执行。在进程启动时,就会有一个类似于 while 循环,没执行一次循环体的过程我们称为 Tick。每个 Tick 的过程就是查看是否有事件待处理,如果有,就取出时间及其相关的回调函数。如果存在关联的回调函数,就执行它们。然后进入下个循环,如果不再有事件处理,就退出进程。
宏任务和微任务
任务队列中的任务可以再细分为宏任务(Macrotask)和微任务(Microtask)。微任务的执行优先级比宏任务的优先级高。
Promise 是微任务,它优先于 setTimeout 宏任务。主线程先输出 “main task!!!”,然后执行微任务,控制台输出 “microtask!!!”,随后输出宏任务 “macrotask!!!”。
回调函数
向服务器请求数据也是异步事件,若我们要获取这个异步事件处理后的结果,需要使用回调函数来获取这个结果。通常,回调函数常常运用于异步事件中。
小明买茶
小明某一天在奶茶店购买一杯柠檬茶,小明心想,与其浪费时间等待,不如刷视频。
小明需要一直等待奶茶店将柠檬茶制作完成,然后将其递给他才算完成整个事件。在等待的过程中,他在刷视频。同一时间内,A 事件需要等待一段时间;B 事件在段时间内完成,这类情况就是异步任务。
在时序图中,我们能很清楚地知道这些事件在一个时间段内应该处于什么位置。小明等待奶茶不应该影响他刷视频,整个体验会非常地友好。至于买的奶茶质量好与不好,不是我们关注的重点。
实际运用
以“小明买茶”故事为例,我们抽取一个函数。第一个参数是异步事件的名称,第二个参数异步事件执行的时间,第三个参数是回调函数作为参数传递。
为什么函数可以作为值进行参数传递,详细请看[JS]函数作为值。
function doSomething(eventName, timeout, callback) {
console.log(`'${eventName}'正在进行中...`)
setTimeout(() => {
callback(`'${eventName}'事件已完成`) // 执行回调函数,提供一点信息。
}, timeout)
}
// 异步事件
doSomething('小明购买奶茶', 3000, (info) => {
console.log(info)
doSomething('小明和朋友聊天', 1000, (info) => {
console.log(info)
})
})
// 同步事件
console.log('与此同时小明正在刷视频!')
异步事件处理完后,将结果传递给回调函数,所以 doSomething 函数是可以接收到回调函数传递过来的包含了结果的参数 info。
但是,在源代码中,回调函数中可以内嵌多个异步事件,这样层层嵌套在代码层面上会出现“倒金字塔”现象,不利于程序员后期的维护,这种现象叫作回调地狱。
回调地狱
在“小明买茶”的案例中,回调内部再嵌套回调,其代码形状上看着像 180° 旋转之后的金字塔,这种层层嵌套就是回调地狱。
但是Promise可以解决回调地狱的问题。Promise 是一个对象,用于表示一个异步操作的最终完成(或失败)及其结果值。
function doSomething(eventName, timeout) {
console.log(`奶茶店接到'${eventName}'的订单,正在制作奶茶中...`)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`'${eventName}'事件已完成`)
}, timeout)
})
}
// 异步任务1
let promise01 = doSomething('小明购买奶茶', 3000).then((info) => {
console.log(info)
})
// 异步任务2
let promise02 = doSomething('小明和朋友聊天', 1000).then((info) => {
console.log(info)
})
// 同步任务
console.log('与此同时小明正在刷视频!')
使用 Promise 之后,解决了多层回调函数调用导致的“倒金字塔”现象。让我们看看实现效果:
JavaScript 异步编程(一):认识异步编程的更多相关文章
- C# 异步编程2 EAP 异步程序开发
在前面一篇博文记录了C# APM异步编程的知识,今天再来分享一下EAP(基于事件的异步编程模式)异步编程的知识.后面会继续奉上TPL任务并行库的知识,喜欢的朋友请持续关注哦. EAP异步编程算是C#对 ...
- 多线程编程学习笔记——异步调用WCF服务
接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...
- C#高级编程9-第13章 异步编程
异步编程 1)异步编程的重要性 在C#5.0中提供了关键字:async和await 使用异步编程后台运行方法调用,程序的运行过程中就不会一直处于等待中.便于用户继续操作. 异步编程有3种模式:异步模式 ...
- php为什么需要异步编程?php异步编程的详解(附示例)
本篇文章给大家带来的内容是关于php为什么需要异步编程?php异步编程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我对 php 异步的知识还比较混乱,写这篇是为了 ...
- Java 异步编程 (5 种异步实现方式详解)
同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现@mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Comp ...
- 《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步IO
同步设备IO与异步设备IO之异步IO介绍 设备IO与cpu速度甚至是内存访问相比较都是比较慢的,而且更不可预测.虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序. 同步IO时,发出IO请求 ...
- 《高性能javascript》 领悟随笔之-------DOM编程篇
<高性能javascript> 领悟随笔之-------DOM编程篇一 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...
- JavaScript 学习笔记之线程异步模型
核心的javascript程序语言并没有包含任何的线程机制,客户端javascript程序也没有任何关于线程的定义,事件驱动模式下的javascript语言并不能实现同时执行,即不能同时执行两个及以上 ...
- 让你高效的理解JavaScript中的同步、异步和事件循环
"同步请求","异步请求"相信这两词在程序猿的世界中频频出现,到底是词性的妖娆,还是撸代码的基础要求,下面直接分享本人学习的好东西,保证让你深入浅出,爽得不要不 ...
- 【转载】Javascript里面的线程和异步
JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序. 参考这篇文章 http://www.ruanyifeng.com/blog/2012/1 ...
随机推荐
- Unicode和中午互转
import java.io.UnsupportedEncodingException; public class TestUnicode{ /* * 中文转unicode编码 */ public s ...
- SpringCloud 简介
目录 什么是微服务? 初识 SpringCloud SpringCloud VS Dubbo 什么是微服务? <互联网系统架构演变> "微服务"一词源于 Martin ...
- 清除 GitHub 历史记录的隐私信息
清理 github 敏感信息 有的时候我们在提交到github上的内容不小心含有敏感代码,比如密码,公司的服务器IP等.这个时候就要通过一些手段清除这些信息. GitHub官方方案比较码放,所以推荐使 ...
- python中 OS模块中 os.path.join() 函数用法简介
基础用法 os.path.join() 用于拼接文件的路径,可以传入多个待拼接的路径 若各个路径之间不存在 " / ", 则其会自动为各个路径之间增加连接符 " / &q ...
- 记录一个奇葩 bug [Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)]
关于 flask 的一个记录 代码 @auth.login_required @app.route('/add', methods=['POST']) def add(): if request.me ...
- vue大型电商项目尚品汇(后台篇)day04
昨天太晚就没来得及更新,今天是spu管理界面,这个界面一共有三个界面需要切换,完成了两个界面,而且今天的难度在于最后两个章节,富有一定的逻辑性,当然中间也有很多需要注意的,比如ElementUI的照片 ...
- ES5的继承和ES6的继承有什么区别?让Babel来告诉你
如果以前问我ES5的继承和ES6的继承有什么区别,我一定会自信的说没有区别,不过是语法糖而已,充其量也就是写法有区别,但是现在我会假装思考一下,然后说虽然只是语法糖,但也是有点小区别的,那么具体有什么 ...
- github package的使用教程
一.写在前面 上一次,笔者向大家介绍了把gitlab仓库作为npm私包的使用方法,具体的详见我的博文地址https://www.cnblogs.com/cnroadbridge/p/16406476. ...
- Redis 内存优化神技,小内存保存大数据
大家好,我是「码哥」,大家可以叫我靓仔. 这次码哥跟大家分享一些优化神技,当你面试或者工作中你遇到如下问题,那就使出今天学到的绝招,一招定乾坤! 如何用更少的内存保存更多的数据? 我们应该从 Redi ...
- 关于webapi调用wcf并发假死的分析
原来IDFA(IOS推广获取到用户IOS手机的唯一标识,如果不刷机的话跟安卓的IMEI一样)在公司正常的页面是公用用一个网站和数据库的. 起初怀疑并发数太多,把数据库连接池的数量从一百设置到三百,确实 ...