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 代码编译为旧浏览器也支持的语法。

ES2017 中的 Async 和 Await的更多相关文章

  1. es2017中的async和await要点

    1. async和await最关键的用途是以同步的写法实现了异步调用,是对Generator异步方法的简化和改进.使用Generator实现异步的缺点如下: 得有一个任务执行器来自动调用next() ...

  2. [译] C# 5.0 中的 Async 和 Await (整理中...)

    C# 5.0 中的 Async 和 Await [博主]反骨仔 [本文]http://www.cnblogs.com/liqingwen/p/6069062.html 伴随着 .NET 4.5 和 V ...

  3. 在MVC中使用async和await的说明

    首先,在mvc中如果要用纯异步请不要使用async和await,可以直接使用Task.Run. 其次,在mvc中使用async和await可以让系统开新线程处理Task的代码,同时不必等Task执行结 ...

  4. C# 中的Async 和 Await 的用法详解

    众所周知C#提供Async和Await关键字来实现异步编程.在本文中,我们将共同探讨并介绍什么是Async 和 Await,以及如何在C#中使用Async 和 Await. 同样本文的内容也大多是翻译 ...

  5. ES7中的async和await

    ES7中的async和await 在上一章中,使用Promise将原本的回调方式转换为链式操作,这就将一个个异步执行的操作串在一条同步线上了.下一次的操作必须等待当前操作的结束. 使用Promise的 ...

  6. async和await的使用总结 ~ 竟然一直用错了c#中的async和await的使用。。

    对于c#中的async和await的使用,没想到我一直竟然都有一个错误.. ..还是总结太少,这里记录下. 这里以做早餐为例 流程如下: 倒一杯咖啡. 加热平底锅,然后煎两个鸡蛋. 煎三片培根. 烤两 ...

  7. ES2017中的async函数

    前面的话 ES2017标准引入了 async 函数,使得异步操作变得更加方便.本文将详细介绍async函数 概述 async 函数是 Generator 函数的语法糖 使用Generator 函数,依 ...

  8. .NET中的async和await关键字使用及Task异步调用实例

    其实早在.NET 4.5的时候M$就在.NET中引入了async和await关键字(VB为Async和Await)来简化异步调用的编程模式.我也早就体验过了,现在写一篇日志来记录一下顺便凑日志数量(以 ...

  9. C# 中的 Async 和 Await

    这篇文章由Filip Ekberg为DNC杂志编写. 自跟随着.NET 4.5 及Visual Studio 2012的C# 5.0起,我们能够使用涉及到async和await关键字的新的异步模式.有 ...

随机推荐

  1. Serv-U精简版FTP服务端

    Window搭建自己的FTP,Serv-U是很不错的选择... 00.运行视图 注意: 此版本是绿色破解版,个人使用很便捷的.不建议商业使用... download: https://pan.baid ...

  2. Ubuntu与Windows7双系统下, 系统时间不一致的问题

    Ubuntu使用的UTC时间, 而Windows使用的是Local Time, 就导致每次切换系统后, Windows时间都会正好晚8个小时. 有两种解决办法, 一个是修改Ubuntu, 另一个是修改 ...

  3. Android API之android.content.BroadcastReceiver

    android.content.BroadcastReceiver Base class for code that will receive intents sent by sendBroadcas ...

  4. EntityFramework 5.0 CodeFirst 教程01-搭建环境和快速上手

    ----------------------------目录------------------------------ EntityFramework 5.0 CodeFirst 教程03-数据结构 ...

  5. 线程同步之mutex和event区别

    之前只是用过 关键段来对同进程不同线程进行互斥,防止对同一份资源或代码段的竞争: mutex可以理解为不同进程或者同一进程内防止对同一份资源的竞争: event更多的是同步,当然也是不同进程或者同一进 ...

  6. Mysql5.6 make 错误以及解决办法

    1.若make出现类似错误: CMake Error: CMake was unable to find a build program corresponding to "Unix Mak ...

  7. Java 时间相比较

    private String getRunTime() { String start = new Date.getTime(); String end= new Date.getTime(); lon ...

  8. 搞不懂为什么开发人员爱iOS恨Android?

    导读:很多网站发表文章大同小异.唯有这个不同点,给大家分享. Android和iOS的较量一直都是人们津津乐道的话题.两个平台各有各的优势所在,同时也都力图能在各个方面赶超对手.对于用户来说,青菜萝卜 ...

  9. hibernate使用注解设置日期默认值

    用注解设置属性的默认值时 使用 @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false,nullable=false,length=20 ...

  10. Charles 网络抓包工具

    1.Charles 简介 Charles 是在 Mac.Linux 或 Windows 下常用的 http 协议网络包截取工具,在平常的测试与调式过程中,掌握此工具就基本可以不用其他抓包工具了.Cha ...