前言

“异步”的大规模流行是在 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 异步编程(一):认识异步编程的更多相关文章

  1. C# 异步编程2 EAP 异步程序开发

    在前面一篇博文记录了C# APM异步编程的知识,今天再来分享一下EAP(基于事件的异步编程模式)异步编程的知识.后面会继续奉上TPL任务并行库的知识,喜欢的朋友请持续关注哦. EAP异步编程算是C#对 ...

  2. 多线程编程学习笔记——异步调用WCF服务

    接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...

  3. C#高级编程9-第13章 异步编程

    异步编程 1)异步编程的重要性 在C#5.0中提供了关键字:async和await 使用异步编程后台运行方法调用,程序的运行过程中就不会一直处于等待中.便于用户继续操作. 异步编程有3种模式:异步模式 ...

  4. php为什么需要异步编程?php异步编程的详解(附示例)

    本篇文章给大家带来的内容是关于php为什么需要异步编程?php异步编程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我对 php 异步的知识还比较混乱,写这篇是为了 ...

  5. Java 异步编程 (5 种异步实现方式详解)

    ​ 同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现@mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Comp ...

  6. 《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步IO

    同步设备IO与异步设备IO之异步IO介绍 设备IO与cpu速度甚至是内存访问相比较都是比较慢的,而且更不可预测.虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序. 同步IO时,发出IO请求 ...

  7. 《高性能javascript》 领悟随笔之-------DOM编程篇

    <高性能javascript> 领悟随笔之-------DOM编程篇一 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...

  8. JavaScript 学习笔记之线程异步模型

    核心的javascript程序语言并没有包含任何的线程机制,客户端javascript程序也没有任何关于线程的定义,事件驱动模式下的javascript语言并不能实现同时执行,即不能同时执行两个及以上 ...

  9. 让你高效的理解JavaScript中的同步、异步和事件循环

    "同步请求","异步请求"相信这两词在程序猿的世界中频频出现,到底是词性的妖娆,还是撸代码的基础要求,下面直接分享本人学习的好东西,保证让你深入浅出,爽得不要不 ...

  10. 【转载】Javascript里面的线程和异步

    JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序. 参考这篇文章 http://www.ruanyifeng.com/blog/2012/1 ...

随机推荐

  1. 拙见--springMVC的controller接受的请求参数

    1-这种是最常用的表单参数提交,ContentType指定为application/x-www-form-urlencoded,也就是会进行URL编码. 1.1-对象类型实体Bean接收请求参数(表单 ...

  2. SQL server设置连接数

    SQLServer查看及设置最大连接数   很多时候自己本地开发会遇到 ,打开几个连接正常访问 之后就报错误,这时候需要调整sqlserver 最大连接数. 1. 查询最大连接数 SELECT val ...

  3. jQuery做轮播图

    这是我自己做的一个简单的轮播图,效果图如下: 我觉得轮播图中最主要的是要理解到图片为什么会滑动,是怎么控制的.上面这个我自己做的,是搭好的一个结构,在无序列表中插入需要的图片.然后在插入图片的后面添加 ...

  4. $.fn解析

    $.fn是指jquery的命名空间,加上fn上的方法及属性,会对jquery实例每一个有效. 如扩展$.fn.abc(),即$.fn.abc()是对jquery扩展了一个abc方法,那么后面你的每一个 ...

  5. Python实现12种概率分布(附代码)

    今天给大家带来的这篇文章是关于机器学习的,机器学习有其独特的数学基础,我们用微积分来处理变化无限小的函数,并计算它们的变化:我们使用线性代数来处理计算过程:我们还用概率论与统计学建模不确定性. 在这其 ...

  6. jenkins页面一直在Please wait while Jenkins is getting ready to work ...

    原因:因为访问官网太慢.我们只需要换一个源,不使用官网的源即可. 1.找到jenkins工作目录 find / -name *.UpdateCenter.xml 2.修改文件中的url,随后重启就行了 ...

  7. 【FAQ】华为帐号服务报错 907135701的常见原因总结和解决方法

    很多开发者在接入华为帐号服务时,经常会出现907135701的报错.根据官网文档说明,错误码907135701表示: 这个错误码在安卓和鸿蒙上都会出现,导致该报错的原因有很多,开发者可以按照下面几点进 ...

  8. Unity3D学习笔记6——GPU实例化(1)

    目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次 ...

  9. shell判断参数值是否在数组内的方法

    比如定义数组: arr=("one" "tow" "thr" "three" "four") 1. ...

  10. CSS Houdini:用浏览器引擎实现高级CSS效果

    vivo 互联网前端团队-Wei Xing Houdini被称之为Magic of styling and layout on the web,看起来十分神秘,但实际上,Houdini并非什么神秘组织 ...