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

ES2017 中的 Async 和 Await

ES2017 在 6 月最终敲定了,随之而来的是广泛的支持了我最喜欢的最喜欢的JavaScript功能: async(异步) 函数。如果你也曾为异步 Javascript 而头疼,那么这个就是为你设计的。如果你没有的话,那么你有可能是个天才。

Async(异步) 函数或多或少允许你编写顺序的 JavaScript 代码,而无需将所有逻辑包装在 callbacks(回调),generators(生成器) 或 promises 中。 考虑一下这个代码:

function logger() {
    let data = fetch('http://sampleapi.com/posts') console.log(data) }   logger()

这段代码没有按照你的预期执行。如果你写过 JS 的话,你可能知道上面的代码为什么不会按预期运行。

但是这个代码确实做了你所期望的。

async function logger() { let data = await fetch('http:sampleapi.com/posts') console.log(data) }   logger()

直观(和漂亮)的代码能够正常运行,而且只添加了两个关键字!

ES6 之前的异步 JavaScript

在我们深入学习 async 和 await 之前,有必要先了解一下 promises 。要弄懂 promises,我们需要再回到简单的回调。

在ES6中引入了 Promises ,并对在 JavaScript 中编写异步代码做了很大的改进。不再有所谓的 “回调地狱”,让我们对 Promises 产生了一点亲切感。

回调是一个函数,可以将结果传递给函数并在该函数内进行调用,以响应任何事件。 这是JS的基础。

function readFile('file.txt', (data) => {
  // 回调函数内部
  console.log(data) }

这个函数只是简单的从一个文件记录数据,在文件完成之前进行读取是不可能的。看起来很简单,但是如果你想按顺序读取和记录五个不同的文件怎么办?

在 Promises 出现之前,为了执行顺序任务,你需要嵌套回调,如下所示:

// 这就是标准的回调地狱
function combineFiles(file1, file2, file3, printFileCallBack) {
    let newFileText = ''
    readFile(string1, (text) => {
        newFileText += text
        readFile(string2, (text) => { newFileText += text readFile(string3, (text) => { newFileText += text printFileCallBack(newFileText) } } } }

很难理解和跟踪代码。这还不包括可能出现的错误处理,比如其中一个文件不存在。

Promise 使这种情况变的更好

这时 Promise 就派上用场了。Promise 是对尚未存在的数据进行推理的一种方法。你所不知道的 JavaScript 系列 的作者 Kyle Simpson 以异步 JavaScript 演讲而闻名。他对 Promise 的 解释 是:这就像是在快餐店里点餐。

  1. 点餐。
  2. 付钱并获得取餐小票。
  3. 等餐。
  4. 当餐准备好了,他们会叫你的单号提醒你取餐。
  5. 取餐。

正如他指出的,当你在等餐的时候,你是不可能吃你的午餐,但是你可以盼它,你可以为你的午餐做好准备。当你等餐的时候,你可以进行其它事情,即使现在没有拿到菜,但是这个午餐已经 “promise” 给你了。这就是所谓的 Promise。一个用于表示终将出现数据的对象。

readFile(file1)
  .then((file1-data) => { /* do something */ })
  .then((previous-promise-data) => { /* do the next thing */ }) .catch( /* handle errors */ )

这是 Promise 的语法。它主要的优点就是可以将队列事件以一种直观的方式链接在一起。虽然这个示例清晰易懂,但是还是用到了回调。Promise 只是让回调显得比较简单和更加直观。

最佳(且最新)方式: Async / Await

几年前,async 函数被纳入了 JavaScript 生态系统。截止上个月,async 函数成为了 JavaScript 语言的官方特性,并得到了广泛支持。

async 和 await 关键字基于 pormise 和 generator 做了简单的封装。本质上,它允许我们在所需的任意位置使用 await 关键字来“暂停”一个函数。

async function logger() { // 暂停直到获取到返回数据 let data = await fetch('http://sampleapi.com/posts') console.log(data) }

这段代码会按照你所期望的那样运行。 它记录了来自 API 调用的数据。如果你连这个都理解困难,那我也不知道咋办了。

这样做的好处是非常直观。 你以大脑思考的方式编写代码,然后告诉代码在所需的位置暂停。

另一个好处就是可以使用 promise 不能使用的 try 和 catch :

async function logger () { try { let user_id = await fetch('/api/users/username') let posts = await fetch('/api/`${user_id}`') let object = JSON.parse(user.posts.toString()) console.log(posts) } catch (error) { console.error('Error:', error) } }

这是一个故意写错的例子,为了证明了一点:catch 将捕获在该过程中的任何步骤发生的错误。至少有 3 个地方 try 可能会失败,这是在异步代码中的一种最干净的方式来处理错误。

我们也可以使用 async 函数让循环和条件判断不再令人头疼:

async function count() { let counter = 1 for (let i = 0; i < 100; i++) { counter += 1 console.log(counter) await sleep(1000) } }

这是一个愚蠢的例子,但这将会按照预期运行并且容易阅读。 如果您在控制台中运行此操作,你会看到代码在调用 sleep 的时候暂停,下一个循环也不会等一秒钟再启动。

一些要注意的细节

现在,你应该已经确信 async 和 await 的美妙之处,接下来我们深入了解一些细节:

  • async 和 await 基于 promise 的。 使用 async 的函数将会始终返回一个 promise 对象。 这一点很重要,要记住,这可能是你遇到的容易犯错的地。
  • 在使用 await 的时候我们暂停了函数,而非整段代码。
  • async 和 await 是非阻塞的。
  • 你仍然可以使用 Promise 例如 Promise.all()。正如我们之前的例子:
async function logPosts () { try { let user_id = await fetch('/api/users/username') let post_ids = await fetch('/api/posts/${user_id}') let promises = post_ids.map(post_id => { return fetch('/api/posts/${post_id}') } let posts = await Promise.all(promises) console.log(posts) } catch (error) { console.error('Error:', error) } }
  • await 只能用于被声明为 async 的函数。
  • 因此,不能在全局范围内使用 await
// 抛出异常
function logger (callBack) { console.log(await callBack) }   // 正常工作 async function logger () { console.log(await callBack) }

现在就可以用啦

截至2017年6月,几乎所有浏览器都可以使用 async 和 await 关键字。为了确保你的代码随时可用,则需要使用 Babel 将你的 JavaScript 代码编译为旧浏览器也支持的语法。

转载于:https://www.cnblogs.com/axl234/p/7423388.html

相关文章:

  • WIN32_LEAN_AND_MEAN 含义以及用法
  • 进程池与列表循环多参传递
  • maven常见问题解决方法
  • Linux中文件MD5校验
  • IAAS,SAAS,PAAS, CaaS的区别
  • 对比 javascript url编码
  • Android -- Adapter
  • 基于webpack的几种静态资源的引入方案
  • redis集群搭建
  • 智能盒子大比拼:Fire TV vs. Apple TV vs. Roku 3 vs. Chromecast
  • 数据挖掘算法Analysis Services-基于SQL Server的数据挖掘
  • DataBase in Android
  • 日志收集方式总结(转载)
  • 编译脚本支持(Build script support)
  • Docker 后台进程参数-------更改Docker运行根目录的方法
  • [译]Python中的类属性与实例属性的区别
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • k8s如何管理Pod
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • React-Native - 收藏集 - 掘金
  • React系列之 Redux 架构模式
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • vuex 笔记整理
  • yii2中session跨域名的问题
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 猴子数据域名防封接口降低小说被封的风险
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 普通函数和构造函数的区别
  • 数组的操作
  • 一个JAVA程序员成长之路分享
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 源码安装memcached和php memcache扩展
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • Spring Batch JSON 支持
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • ​虚拟化系列介绍(十)
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (007)XHTML文档之标题——h1~h6
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2)STM32单片机上位机
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (三)c52学习之旅-点亮LED灯
  • (十一)图像的罗伯特梯度锐化
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?