ES2017异步函数现已正式可用
ES2017异步函数现已正式可用
葡萄城控件技术团队 发表于3个月前
ES2017异步函数现已正式可用
  • 发表于 3个月前
  • 阅读 2450
  • 收藏 71
  • 点赞 5
  • 评论 16

腾讯云 学生专属云服务套餐 10元起购>>>   

ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数。如果你曾经被异步 JavaScript 的逻辑困扰,这么新函数正是为你设计的。

异步函数或多或少会让你编写一些顺序的 JavaScript 代码,但是却不需要在 callbacks、generators 或 promise 中包含你的逻辑。

如下代码:

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()

这段代码起作用了,从直观上看,仅仅只是多了 async 和 await 两个词。

 

ES6 标准之前的 JavaScript 异步函数

在深入学习 async 和 await 之前,我们需要先理解 Promise。为了领会 Promise,我们需要回到普通回调函数中进一步学习。

Promise 是在 ES6 中引入的,并促使在编写 JavaScript 的异步代码方面,实现了巨大的提升。从此编写回调函数不再那么痛苦。

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

function readFile('file.txt', (data) => {
    // This is inside the callback function
    console.log(data)
}

这个函数只是简单的向文件中记录数据,在文件完成之前进行读取是不可能的。这个过程似乎很简单,但是如果想要按顺序读取并记录五个不同的文件,需要怎么实现呢?

没有 Promise 的时候,为了按顺序执行任务,就需要通过嵌套回调来实现,就像下面的代码:

// This is officially callback hell
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 是对还未产生的数据的一种推理。Kyle Simpson 将 Promise 解释为:就像在快餐店里点餐一样。

  • 点餐
  • 为所点的午餐付费,并拿到排队单号
  • 等待午餐
  • 当你的午餐准备好了,会叫你的单号提醒你取餐
  • 收到午餐

正如上面的这种场景,当你等餐时,你是无法吃到午餐的,但是你可以提前为吃午餐做好准备。你可以进行其它事情,此时你知道午餐就要来了,虽然此刻你还无法享用它,但是这个午餐已经“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 是建立在 Promise 和 generator上。本质上,允许我们使用 await 这个关键词在任何函数中的任何我们想要的地方进行暂停。

async function logger() {
    // pause until fetch returns
    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 可以捕获任何步骤中发生的错误。至少有三个地方,try 可能会失败,这是在异步代码中的一种最干净的方式来处理错误。

我们还可以使用带有循环和条件的 async 函数:

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

这是一个很简答的例子,如果运行这段程序,将会看到代码在 sleep 调用时暂停,下一个循环迭代将会在1秒后启动。

 

要点和细节

相信我们已经感受到了 asyns 和 await 的美妙之处,接下来让我们深入了解一下细节:

  • async 和 await 建立在 Promise 之上。使用 async,总是会返回一个 Promise。请记住这一点,因为这也是容易犯错的地方。
  • 当执行到 await 时,程序会暂停当前函数,而不是所有代码
  • async 和 await 是非阻塞的
  • 依旧可以使用 Promise helpers,例如 Promise.all( )

正如之前的示例:

async function logPosts ()  {
    try {
        let user_id = await fetch('/api/users/username')
        let post_ids = await fetch('/api/posts/<code>${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

如下代码:

// throws an error
function logger (callBack) {
    console.log(await callBack)
}

// works!
async function logger () {
    console.log(await callBack)
}

 

现已正式可用

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

如果对更多ES2017内容感兴趣,请访问ES2017特性的完整列表

JavaScript 开发工具推荐

SpreadJS 纯前端表格控件是基于 HTML5 的 Java 电子表格和网格功能控件,提供了完备的公式引擎、排序、过滤、输入控件、数据可视化、Excel 导入/导出等功能,适用于 .NET、Java 和移动端等各平台在线编辑类 Excel 功能的表格程序开发。

原文链接:https://css-tricks.com/using-es2017-async-functions/

转载请注明出自:葡萄城控件

标签: ES2017 JavaScript
共有 人打赏支持
葡萄城控件技术团队
粉丝 302
博文 461
码字总数 635750
作品 13
评论 (16)
Fenying
在TS里已经用了很久了
阿爆g
我感觉越来越多的技术 都在模仿 微软的技术风格
在下路哥

引用来自“Fenying”的评论

在TS里已经用了很久了
ES2017应该是ES8吧?
dolloyo
ES2017出来了,说明我们可以开始用ES2019甚至ES2020了。
昵称非法已被屏蔽

引用来自“Fenying”的评论

在TS里已经用了很久了
async/await
Python 3.5 2015 开始用
.Net 4.5 2012 开始用
sikele2237
当年喷这玩意的人海了去了,一个个都显得很牛逼,说这事语法糖没用,现在都没啥声音了
angelboy

引用来自“sikele2237”的评论

当年喷这玩意的人海了去了,一个个都显得很牛逼,说这事语法糖没用,现在都没啥声音了
对啊,出的晚至少出了,用了aa之后觉得co真特么s b
伊森

引用来自“Fenying”的评论

在TS里已经用了很久了
下定决心切TS了
xioxin
最后不忘加个广告
BruceWan
C# 确实有许多先进的语言思想
Klaus88

引用来自“angelboy”的评论

引用来自“sikele2237”的评论

当年喷这玩意的人海了去了,一个个都显得很牛逼,说这事语法糖没用,现在都没啥声音了
对啊,出的晚至少出了,用了aa之后觉得co真特么s b

@angelboy co哪里s b了?
angelboy

引用来自“Klaus88”的评论

引用来自“angelboy”的评论

引用来自“sikele2237”的评论

当年喷这玩意的人海了去了,一个个都显得很牛逼,说这事语法糖没用,现在都没啥声音了
对啊,出的晚至少出了,用了aa之后觉得co真特么s b

@angelboy co哪里s b了?
用用aa后知道啊,co太繁琐了
雷兽
async awiat 最初绝对不是出现在c# .net里 但是常见语言里最早推广这个的是微软.net c# 没错。。。
JhonWei
打赏成功,飘窗居然显示支付失败,什么APP哦
gonnavis
这功能超赞啊! 我继续用Promise。
曾建凯
async真的让js开发有了可以和现有编程语言一分天下的意思。匿名函数和promise实在差点意思。
×
葡萄城控件技术团队
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: