@async 如何返回list_图解 Await 和 Async

原文链接:Await and Async Explained with Diagrams and Examples

文章目录

  1. 简介
  2. Promise
  3. 问题:组合 Promise
  4. Async 函数
  5. Await
  6. 错误处理
  7. 讨论

简介

JavaScript ES7中的 async/await 使得协调异步 promise 变得更容易。如果你需要从多个数据库或 API 异步获取数据,则可以使用 promise 和回调函数。async / await 使我们更简洁地表达这种逻辑,并完成更易读和可维护的代码。

本教程将使用图表和简单示例来解释 JavaScript中 的 async / await 语法。

在讲解之前,我们从 promises 的简要概述开始。如果你已经了解了 JS 中的 promise,请随时跳过本节。

Promises

在 JavaScript 中,promise 代表非阻塞异步执行的抽象对象。JS中的 promise 与 Java 中的Future 或C#的Task类似,如果你了解它们的话那就很容易理解。

Promise 通常用于网络和 I/O 操作,例如读取文件或者发出 HTTP 请求。我们可以产生一个异步 promise ,并使用 then 的方法来附加一个回调函数,这个回调函数当 promise 完成时将会被触发,这种方法不会阻止当前的“线程”执行。回调函数本身可以返回 promise,使我们可以有效地链接 promises

为了容易理解,在所有示例中,我们假设 request-promise 库已经安装并加载为:

var rp = require('request-promise');

我们做一个简单的 HTTP GET 请求,返回一个promise:

const promise = rp('http://example.com/')

现在,让我们来看一个例子:

console.log('Starting Execution');const promise = rp('http://example.com/'); // Line 3
promise.then(result => console.log(result)); // Line 4console.log("Can't know if promise has finished yet...");

我们在第3行产生了一个新的 Promise,然后在第4行新加一个回调函数。因为promise 是异步的,所以当我们到达第6行时,我们不知道 promise 是否已经完成。如果我们多次运行代码,我们可能会每次得到不同的结果。换句话说,任何 promise 之后的代码都是与 promise 同时运行的。

promise 完成之前,并没有办法阻止当前的操作顺序。 这与 Java 中的 Future.get 不同,其允许我们阻止当前线程,然后之后完成。在 JavaScript 中,我们不能等待 promise。在 promise 之后调度代码的唯一方法是通过 then 附加回调函数。

下图描绘了该示例的计算过程:

5a1a51c936f7091edd5a55322aeb4cb7.png
promise 的计算过程。呼叫“线程”不能等待 promise 。在 promise 之后调度代码的唯一方法是通过then方法指定回调函数。

当 promise 成功返回时,只有通过then方法指定回调函数才能执行。如果它失败了(例如由于网络错误),回调函数将不会执行。为了处理失败的 promise ,你可以通过catch附加另一个回调函数:

rp('http://example.com/').then(() => console.log('Success')).catch(e => console.log(`Failed: ${e}`))

最后,为了测试一下,我们可以使用Promise.resolvePromise.reject方法创建成功或失败的“虚拟”promises:

const success = Promise.resolve('Resolved');
// 将会显示 "Successful result: Resolved"
success.then(result => console.log(`Successful result: ${result}`)).catch(e => console.log(`Failed with: ${e}`))const fail = Promise.reject('Err');
// 将会显示 "Failed with: Err"
fail.then(result => console.log(`Successful result: ${result}`)).catch(e => console.log(`Failed with: ${e}`))

有关 promises 的更详细的教程,请查看这篇文章

组合 Promise

使用单一 Promise 是简单有效的。但是,当我们需要对复杂的异步逻辑进行编程时,我们可能会以组合多个 Promise。编写所有的子句和匿名回调可能很容易失控。

例如,假设我们需要编写一个程序:

  1. 进行HTTP请求,等待完成,打印结果;
  2. 然后进行其他两个并行HTTP调用;
  3. 当它们都完成时,打印结果。 以下代码段演示了如何完成此操作:
//进行第一个调用
const call1Promise = rp('http://example.com/'); // Line 2call1Promise.then(result1 => {//在第一个请求完成后执行console.log(result1);const call2Promise = rp('http://example.com/'); // Line 8const call3Promise = rp('http://example.com/'); // Line 9return Promise.all([call2Promise, call3Promise]); // Lin 11
}).then(arr => { // Line 12//两个 promise 完成后执行console.log(arr[0]);console.log(arr[1]);
})

我们首先进行第一个 HTTP 调用,并使用回调以在其完成时运行(第1-3行)。在回调中,我们为后续的 HTTP 请求产生了两个 Promise(第8-9行)。这两个 Promise 同时运行并且我们需要安排一个回调,当它们都完成时。因此,当它们都执行完成时,我们需要通过Promise.all(第11行)将它们组合成一个单一的 Promise。回调的结果是一个 Promise,我们需要l连接另一个回调函数来打印结果(行12-16)。

下图描述了计算流程:

5b89b2d3e88fc3932eb82f04ca24e1e4.png
计算过程中的 Promise 组合。我们使用“Promise.all”将两个并发的 Promise组合成一个 Promise。

对于这样一个简单的例子,我们最终得到了 2 个回调,并且必须使用Promise.all来同步并发Promise。如果我们不得不再运行一些异步操作或添加错误处理怎么办?这种方法最终很容易崩溃于then-s,Promise.all调用和回调三者混杂在一起。

Async

异步函数是用于定义返回Promise的函数的快捷方式。 例如,以下定义是等价的:

function f() {return Promise.resolve('TEST');
}// asyncF相当于f!
async function asyncF() {return 'TEST';
}

类似地,抛出异常的异步函数等效于返回拒绝 Promise 的函数:

function f() {return Promise.reject('Error');
}// asyncF相当于f!
async function asyncF() {throw 'Error';
}

Await

当我们使用 promise 之后,我们只能通过then来传回回调函数(callback)。 不允许直接等待一个 promise 执行完毕是为了鼓励用户书写非阻塞的代码,不然用户会更乐意写阻塞的代码,因为它比 promise 和回调函数更简单。

然而,为了同步 promise, 我们需要允许 promise 之间相互等待。换句话说,如果一个异步的操作(例如封装在一个 promise 中)就应该去等待另一个异步的操作去完成。但是 JavaScript 解释器如何判断一个操作是否在 promise 中运行呢?

答案就是 async 关键字。每一个 async 函数都会返回一个 promise。也就是说, JavaScript 解释器就会把所有在 aysnc 函数中的操作封装到 promise 中并异步运行。这样就可以让它们去等待其他的 promise 完成。

按下 await 关键字,await 只能在 async 函数中使用,作用是让我们同步的等待另一个 promise 执行完毕。如果在 async 函数之外使用 promise 的话,依旧需要使用 then 回调函数:

async function f() {// 返回值将作为 promise 被处理(resolve)之后的结果const response = await rp('http://example.com/');console.log(response);
}
// 不能在 async 函数之外使用 await 关键字
// 需要使用 then 回调
f().then(() => console.log('Finished'));

现在,来看看如何解决刚在在上面一节出现的问题:

// 将解决问题的方法封装到一个异步的函数中
async function solution() {// 等待第一个 HTTP 调用并且打印出结果console.log(await rp('http://example.com/'));// 生成 HTTP 调用但是不等待它们执行完毕 - 同时运行const call2Promise = rp('http://example.com/'); // 不等待! // Line 7const call3Promise = rp('http://example.com/'); // 不等待! // Line 8//在它们都被调用之后 - 等待它们执行完毕const response2 = await call2Promise; // Line 11const response3 = await call3Promise; // Line 12console.log(response2);console.log(response3);
}// 调用 async 函数
solution().then(() => console.log('Finished'))

在上面的代码段中,我们将解决方案封装到了一个 async 函数中,这样我们就可以直接的等待(await) promise 执行完毕。这样避免了使用 then 回调函数。 最后,我们调用了 async 函数,这个函数只是简单的生成了一个封装调用其他 promisepromise

在第一个例子(没有 asyncawait)中,那些 promise 会并行启动。这种情况下我们进行了同样的操作(第7,8行)。注意,直到11到12行,我们都没有使用 await。当所有的promise都执行完毕(resolve),我们才去阻塞程序的执行。之后,我们知道两个 promise 都执行完毕了(就像在之前的例子中,使用 Promise.all(...).then(...) 一样)。

在底层的计算过程上,这个过程和先前章节所述的过程是相同的,但是代码更加直观,可读性更好。

在引擎中,async/await 实际上转成了 promise 和 then传入的回调函数。换句话说,它是 promise 的语法糖。每次我们使用 await,解释器就会生成一个 promise,然后把其余的操作从 async 函数取出来放到 then 传入的回调函数中。

考虑一下下面的例子:

async function f() {console.log('Starting F');const result = await rp('http://example.com/');console.log(result);
}

函数f在底层计算过程描述如下图。由于f是异步的,它将和调用者同步运行:

101d6b12f496e9021f183f383cdbad2a.png

函数f开始运行并且生成了一个promise。同时,函数其余的部分被封装到回调函数中,安排在promise执行完毕之后再执行。

错误处理

在前面几个例子中,我们假设promise 成功的解决(reslove).于是,等待一个promise返回结果。事实上,如果等待的promise失败(reject)了,那么async函数将会返回一个异常(exception)。我们可以使用标准的try/catch去处理这种情况:

async function f() {try{const promiseResult = await Promise.reject('Error');} catch (e) {console.log(e);}
}

如果一个async函数没有处理异常,不管它是一个被拒绝(reject)的promise还是其他的 bug 造成的,它将返回一个被拒绝(reject)的promise:

async function f() {// 抛出异常const promiseResult = await Promise.reject('Error');
}// 将打印 "Error"
f().then(() => console.log('Success')).catch(err => console.log(err))async function g() {throw "Error";
}// 将打印 “Error”
g().then(() => console.log('Success')).catch(err => console.log(err))

使用已知的异常处理机制将使我们方便的处理被拒绝(reject)的promise

讨论

Async/awaitpromises的一种补充语言结构。它允许我们使用较少样板的 promise。然而async/await不能取代纯粹 promise 的需要。例如,如果从一个普通的函数或者全局范围内调用一个async函数,我们无法使用await,我们将借助于普通的promises(译者注:原文使用的是vanilla promise):

async function fAsync() {// 事实上返回值是 Promise.resolve(5)return 5;
}
// 不能调用 await fAsync(), 需要使用 then/catch
fAsync().then(r => console.log(`result is ${r}`));

我通常尝试将我大部分的异步逻辑封装到一个或者几个 async 函数中,然后从非异步的代码中调用。这极大地减少了我编写then/catch回调的数量。

async/await 结构是更简洁处理 promise 的语法糖。每一个 async/await 结构都可以使用纯粹的 promise 重写。最终,这是一个风格和简洁方面的问题。

学者们指出并发( concurrency )和并行( parallelism )有区别。查看Rob Pike关于该主题或我之前的帖子。并发是关于组合独立进程(在过程的一般含义中)一起工作,而并行是关于实际上同时执行多个进程。并发是关于应用程序的设计和结构,而并行性就是实际的执行。

以多线程应用程序为例。将应用程序分隔为线程定义其并发模型。这些线程在可用内核上的映射定义了其级别或并行。并发系统可以在单个处理器上有效运行,在这种情况下,它不是并行的。

6b56b6761f8d711b9a215b0663201f70.png

在这种情况下,promise允许我们将程序分解为可并行运行的并发模块。实际的 JavaScript执行是否并行取决于 JavaScript 解释器实现。例如,Node.js是单线程的,如果 promise 是 CPU 绑定的,那么并不会看到很多并行进程。然而,如果您通过类似 Nashorn 的工具将代码编译成 java 字节码,理论上你可以在不同的 CPU 核心上映射CPU绑定的 promise 并实现并行运行。因此,在我看来,promise(普通或通过async/await)构成了JavaScript应用程序的并发模型。

文章转自 | Github
文章链接 | 图解 Await 和 Async

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

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

相关文章

求一个向量变换为另一个向量的矩阵_OpenGL里旋转等变换矩阵为什么是4x4的矩阵...

OpenGL ES 的很多教程里都会有这样一个例子来讲解纹理:将一张图片作为纹理显示在屏幕上。因为纹理坐标和实际屏幕显示的坐标不一样,把图片渲染在屏幕上后,图片是上下颠倒的。一个解决方法是对当前的顶点坐标,乘以绕 z 轴旋转180度…

白鹭时代与html5关系,白鹭时代观点:HTML5行业将进入红利收割期

2016年,H5产业风风火火,各种引人注目。最大成果无疑是游戏《传奇世界H5》月流水破了3000万,引起整个行业巨大震动,同时,也标志着H5重度游戏时代来临。国内最大的HTML5移动技术与服务供应商白鹭时代认为,201…

boot定时任务开启和关闭 spring_Spring之定时任务实践

Java下的定时任务实现有Timer,Spring,QuartZ等,这里我们介绍Spring中定时任务的应用,其通过 Scheduled 注解即可轻松实现概述Spring 的定时任务可以支持各种形式的定时调度任务。其通过加在定时方法上的 Scheduled 注解来配置任务…

计算机怎么取消用户密码,怎么取消开机密码:计算机开机进入Windows时的开机密码怎么取消?...

优质回答 回答者:168长腿欧巴在电脑中的“用户账户”界面即可设置取消开机密码,具体操作请参照以下步骤。1、右键Win10系统的开始菜单,选择弹出菜单里的“运行”选项。2、然后在出现的运行对话框里输入命令“netplwiz”,点击确定。…

echarts symbol 回调函数_【OpenLayer 实战】实现仿Echarts风格的动态迁徙图/航班图

使用的数据还是来自echarts,模拟了全国各地到湖南重点景区的客流情况。分析要实现动态迁徙图的效果,主要需解决两个问题 曲线的绘制。因为给出的数据只有起点和终点两个点位,所以想要绘制曲线可以参考turf中的bezier曲线生成API。 点迹…

校运会计算机科学系大本营,计算机系团总支学生会学期总结大会

(本网讯)时光飞逝,又一个学期即将就此画上了圆满的句号。为推动我系团总支学生会工作更长久的发展,提高学生会干部工作的热情和质量,2016年12月8日晚上19:00在第一教学楼C102课室里,校学生会全体成员齐聚一堂,参加了20…

hexo评论_【前端简历加分】hexo框架搭建个人博客站点,手把手教学

最近,粉丝们在金九银十期间也是不断的面试,有比较多的毕业生说我在这个期间出多一些面试题,上几期都是倾向于面试文章,这期出一次简历加分项操作,使用hexo框架搭建个人博客。作为应届毕业生或者1-3年工作经验的程序员拥…

电脑抓整个路由器的包_网络是电竞游戏体验的命脉 2018年年度电竞路由器功能盘点...

2018年是电竞路由器的元年,从2017年的概念产品诞生开始,电竞路由器很快就确定了品类,随着《绝地求生》、《王者荣耀》等对网络质量敏感的竞技游戏的火爆,电竞路由器迅速被竞技玩家熟知,并在2018年以野火燎原的速度发展…

dos 改某个目录下所有文件的时间_go语言入门学习笔记(2)-DOS操作及go语言变量学习...

API:application program interface:应用程序编程接口。就是我们go的各个包的各个函数。我们想要了解这些函数我们可以通过Go的中文网在线标准库文档:https://studygolang.com/pkgdocDOS介绍:Dos:Disk Operating System 磁盘操作系…

es multi match_PHP 的ES搜索操作

原文:https://blog.csdn.net/JineD/article/details/106650695首先从ES的支持的字段说起,ES文档中字段有多种类型 官方文档。这几个比较常用:text,keyword,integer,float,boolean,object,geo_point(地理坐标),geo_shape(描述地理区域),date.注…

git 怎么还原历史版本_4. Git--修改/回退等操作

》》点赞,收藏关注,理财&技术不迷路《《我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容:Git is a distri…

为什么用python写爬虫_零基础,是怎么开始写Python爬虫的

刚开始接触爬虫的时候,简直惊为天人,十几行代码,就可以将无数网页的信息全部获取下来,自动选取网页元素,自动整理成结构化的文件。利用这些数据,可以做很多领域的分析、市场调研,获得很多有价值…

datatable怎么根据两列分组_谈谈怎么做服务隔离

来源于公众号孤独烟 ,作者孤独烟引言OK,如下图所示那显而易见,做服务隔离的目的就是避免服务之间相互影响。毕竟谁也不能说自己的微服务百分百可用,如果不做隔离,一旦一个服务出现了问题,整个系统的稳定性都…

position: absolute;_前端性能优化--transform与position

上个星期去yy语音面试&#xff0c;就有一个这样问题&#xff1a; transform与position:absolute 有什么区别? 我回家后查资料发现这道题目其实不简单啊&#xff0c;涉及到重排、重绘、硬件加速等网页优化的知识。首先看一个用top、left实现的动画效果<style>html,body {…

台式计算机总是重启,台式电脑经常自动重启怎么修复

当我们的电脑出现了电脑自动重启的时候&#xff0c;我们就要注意了&#xff0c;说明我们的电脑主机出现问题了&#xff0c;怎么解决呢。下面是学习啦小编为大家整理的关于台式电脑经常自动重启的相关资料&#xff0c;希望对您有所帮助!台式电脑经常自动重启的解决方法方法/步骤…

线程执行完之后会释放吗_java多线程并发:CAS+AQS+HashMap+volatile+ThreadLocal,乐分享...

CyclicBarrier、CountDownLatch、Semaphore 的用法CountDownLatch(线程计数器 )CountDownLatch 类位于 java.util.concurrent 包下&#xff0c;利用它可以实现类似计数器的功能。比如有一个任务 A&#xff0c;它要等待其他 4 个任务执行完毕之后才能执行&#xff0c;此时就可以…

计算机应用基础考查方案,《计算机应用基础》考查方案

《计算机应用基础》考查方案 《计算机应用基础》考核方案 制订人&#xff1a;刘久红老师 计算机应用基础科任教师 制订部门&#xff1a;基础课与思政课教学部 制订时间&#xff1a;2012年12月 一&#xff1a;考核依据 计算机应用基础是面向全院各专业开设的一门重要的公共基础课…

无法启动此程序因为计算机丢失msvcr110.dll,计算机中丢失msvcr110.dll怎么办

我们在打开电脑程序的时候&#xff0c;系统提示无法启动此程序&#xff0c;因为计算机中丢失MSVCR110.dll&#xff0c;尝试重新安装该程序以解决此问题。这是什么情况呢?因为现在所有的5.5环境都是基于vc11的编译脚本下生成的&#xff0c;所以在windows下你得安装相关组件&…

anaconda pandas运行不了_学习python你必须弄懂的 Python、Pycharm、Anaconda 三者之间的关系...

Python作为深度学习和人工智能学习的热门语言&#xff0c;学习一门语言&#xff0c;除了学会其简单的语法之外还需要对其进行运行和实现&#xff0c;才能实现和发挥其功能和作用。下面来介绍运行Python代码常用到的工具总结。一.Python、Pycharm、Anaconda关系介绍1. PythonPyt…

java: 程序包com.alibaba.fastjson不存在_Java开发中的异常

NO.1 Java.alng.NullPointerException这个异常大家肯定都经常遇到&#xff0c;异常的解释是 “程序遇上了空指针 “&#xff0c;简单地说就是调用了未经初始化的对象或者是不存在的对象&#xff0c;这个错误经常出现在创建图片&#xff0c;调用数组这些操作中&#xff0c;比如图…