前端开发者丨JavaScript
ES2017 异步函数现已正式可用ES2017标准已于2017年6月份正式定稿了, 并广泛支持最新的特性: 异步函数。
如果你曾经被异步 JavaScript 的逻辑困扰, 这么新函数正是为你设计的。
异步函数或多或少会让你编写一些顺序的 JavaScript 代码, 但是却不需要在 callbacks、 generators 或 promise 中包含你的逻辑。
如下代码:
function logger() {
letdata=fetch(‘http://sampleapi.com/posts’) console.log(data)
}
logger() 1 2 3 4 5
function logger() {
letdata=fetch(‘http://sampleapi.com/posts’) console.log(data)
}
logger() 这段代码并未实现你的预期。
如果你是在js中编写的, 那么你可能会知道为什么。
下面这段代码, 却实现了你的预期。
async function logger() {
letdata=awaitfetch(‘http:sampleapi.com/posts’) console.log(data)
}
logger() 1 2 3 4 5 async function logger() {
letdata=awaitfetch(‘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 ) } } } }这就很难推断函数下面会发生什么,同时也很难处理各种场景下发生的错误,比如其中某个文件不存在的情况。
Promise改善了这种情况这正是Promise的优势所在, Promise是对还未产生的数据的一种推理。
KyleSimpson将Promise解释为: 就像在快餐店里点餐一样。
点餐为所点的午餐付费, 并拿到排队单号等待午餐当你的午餐准备好了, 会叫你的单号提醒你取餐收到午餐正如上面的这种场景, 当你等餐时, 你是无法吃到午餐的, 但是你可以提前为吃午餐做好准备。
你可以进行其它事情, 此时你知道午餐就要来了, 虽然此刻你还无法享用它, 但是这个午餐已经“ promise” 给你了。
这就是所谓的promise, 表示一个最终会存在的数据的对象。
readFile(file1).then((file1 – data) => { /* do something */ }).then((previous – promise – data) => { /* do the next thing */ }).catch( /* handle errors */ ) 1234readFile(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这个关键词在任何函数中的任何我们想要的地方进行暂停。
asyncfunctionlogger() { // 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: asyncfunctionlogger() {
try {
letuser_id=awaitfetch(‘/api/users/username’) letposts=awaitfetch(‘/api/`${user_id}`’) letobject=JSON.parse(user.posts.toString()) console.log(posts)
} catch (error) {
console.error(‘Error:’, error)
}
}
12345678910asyncfunctionlogger() {
try {
letuser_id=awaitfetch(‘/api/users/username’) letposts=awaitfetch(‘/api/`${user_id}`’) letobject=JSON.parse(user.posts.toString()) console.log(posts)
} catch (error) {
console.error(‘Error:’, error)
}
}
上面是一个刻意写错的示例, 为了证明了一点: 在运行过程中,
catch可以捕获任何步骤中发生的错误。
至少有三个地方,
try可能会失败, 这是在异步代码中的一种最干净的方式来处理错误。
我们还可以使用带有循环和条件的async函数: asyncfunctioncount() {
letcounter=1
for (leti=0; i) {
counter+=1console.log(counter) awaitsleep(1000)
}
}
12345678asyncfunctioncount() {
letcounter=1
for (leti=0; i) {
counter+=1console.log(counter) awaitsleep(1000)
}
}
这是一个很简答的例子, 如果运行这段程序, 将会看到代码在sleep调用时暂停, 下一个循环迭代将会在1秒后启动。
要点和细节相信我们已经感受到了asyns和await的美妙之处, 接下来让我们深入了解一下细节: async和await建立在Promise之上。
使用async, 总是会返回一个Promise。
请记住这一点, 因为这也是容易犯错的地方。
当执行到await时, 程序会暂停当前函数, 而不是所有代码async和await是非阻塞的依旧可以使用Promisehelpers, 例如Promise.all() 正如之前的示例: asyncfunctionlogPosts() {
try {
letuser_id=awaitfetch(‘/api/users/username’) letpost_ids=awaitfetch(‘/api/posts/${user_id}’) letpromises=post_ids.map(post_id=> {
returnfetch(‘/api/posts/${post_id}’)
}
letposts=awaitPromise.all(promises) console.log(posts)
}
catch (error) {
console.error(‘Error:’, error)
}
}
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/${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 ) }现已正式可用到2017年6月,几乎所有浏览器都可以使用 async 和 await。
为了确保你的代码随时可用, 则需要使用 Babel 将你的 JavaScript 代码编译为旧浏览器也支持的语法。
如果对更多ES2017内容感兴趣, 请访问ES2017特性的完整列表。
前端开发者丨JavaScript
评论前必须登录!
注册