俄罗斯贵宾会-俄罗斯贵宾会官网
做最好的网站

ES2017 异步函数现已正式可用俄罗斯贵宾会

ES2017 异步函数现已正式可用

2017/08/22 · JavaScript · ES2017, 异步

原文出处: ERIC WINDMILL   译文出处:葡萄城控件   

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

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

如下代码:

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

1
2
3
4
5
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()

1
2
3
4
5
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) }

1
2
3
4
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) } } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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)
            }
        }
    }
}

这就很难推断函数下面会发生什么,同时也很难处理各种场景下发生的错误,比如其中某个文件不存在的情况。

var gen = function* (){
 var a = yield readFile();
 console.log(a);
 var b= yield readFile();
 console.log(b);
}

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 */ )

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

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

接下来用async形式来表示generator函数

最佳方式: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) }

1
2
3
4
5
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) } }

1
2
3
4
5
6
7
8
9
10
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 ) { counter += 1 console.log(counter) await sleep(1000) } }

1
2
3
4
5
6
7
8
async function count() {
    let counter = 1
    for (let i = 0; i ) {
        counter += 1
        console.log(counter)
        await sleep(1000)
    }
}

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

var gen = async function(){
 var a = await readFIle();
 console.log(b);
 var b = await readFile();
 console.log(b);
}

要点和细节

相信我们已经感受到了 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) } }</code>

1
2
3
4
5
6
7
8
9
10
11
12
13
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)
    }
}</code>
  • await 只能用于声明为 async 的函数中
  • 因此,不能在全局范围内使用 await

如下代码:

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

1
2
3
4
5
6
7
8
9
// throws an error
function logger (callBack) {
    console.log(await callBack)
}
 
// works!
async function logger () {
    console.log(await callBack)
}

从以上可以看出async函数就是在generator函数上改进的,就是把*改为async,然后把yield改为await,但是他在generator函数上有一些改进

现已正式可用

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

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

1 赞 收藏 评论

俄罗斯贵宾会 1

哪些改进呢?


  • 内置执行器
    在我的另一篇博客中说出generator函数执行,需要为他写自动执行器,例如基于thunk函数的自动执行器,还有基于promise的自动执行器,还有就是co模块,但是async函数只要调用它就自己执行
  • 更好的语义
  • 更广泛的使用
    co模块的yield语句后面必须是thunk函数或者是promise对象,但是await函数后面可以是原始类型的值
  • 返回的是promise对象
    可以使用then等

使用方法


async function getStockByName(name){
 const symbol = await getStockSymbol(name);
 const stockPrice = await getStockPrice(symbol);
 return stockPrice;
}
getStockByName('sportShoe').then(function(result){
 console.log(result);
})

我们可以看出只要我们调用一次getStockByName(),就自动执行,所以最后的result就是stockPrice

语法


返回Promise对象

调用async函数会返回一个promise对象,所以才可以调用then方法,then方法中的函数的参数就是async函数返回的值

const aw = async function(age){
 var name = await search(age);
 return name;
}
aw(20).then(name => console.log(name));

本文由俄罗斯贵宾会发布于Web前端,转载请注明出处:ES2017 异步函数现已正式可用俄罗斯贵宾会

您可能还会对下面的文章感兴趣: