ES8 的 Async 函数有几种写法?
——理解 Async 函数的本质
async/await 的介绍
Async 函数是 ES2017 内引入的新功能,js 生态圈内也出现大量支持 async 函数的类库和框架,例如 eggjs。
node 对最新 ES 语法支持的对照表: https://node.green。比如我们可以发现类的装饰器目前没有任何版本支持;10.1.0 开始支持 Promise.finally。
例子:显示当前的公网 ip
传统写法
const request = require('request');
request('http://whatismyip.akamai.com', function(error, res, body) {
console.log(body);
});
Async 写法
const request = require('request-promise-native');
const body = await request('http://whatismyip.akamai.com');
console.log(body);
优点
从以上例子可以看出,源码更适应人类思考的模式,具备更好的可读性,降低了维护难度。
兼容性
Node.js 8 及以上的版本已经支持。浏览器的兼容情况可参考 caniuse async functions。列表如下:
- Edge:15+
- Chrome:55+
- Firefox:52+
- Safari:10.1+
- Opera:42+
- iOS Safari:10.3+
- Chrome Android:66+
- Firefox Android:57+
- UC for Android:11.8+
360安全浏览器9.1版本内核已经升级到 55,搜狗浏览器9周年版已经升级内核到 58。它们不用 Babel 的情况下也支持 async/await。
完全不支持的浏览器如下:
- IE
- Opera Mini
- Baidu
学习资料: ECMAScript 6 入门
- 阮一峰编写的《ECMAScript 6 入门》有一章专门介绍了 async 函数。
- 同时需要理解 Promise 对象,这是正确使用 Async 函数的前提。否则容易知其然而不知其所以然。
一、如何在 async 函数里使用 setTimeout
很多人在初次接触到 async 函数时觉得很酷但又一头雾水,更多时候是复制粘贴,照葫芦画瓢。当需要在 async 函数里使用 setTimeout 的场景时,有点不知所措。
错误示范
main 函数等待两秒后返回 done
const main = async () => {
setTimeout(function () {
return 'done';
}, 2000);
};
const result = await main();
console.log(result);
正确做法
const main = () => {
return new Promise((resolve, reject)=>{
setTimeout(function () {
resolve('done');
}, 2000);
});
};
const result = await main();
console.log(result);
仔细对比两个例子,可以发现 async 函数其实就是 Promise 对象的语法糖,它是返回 Promise 对象的普通 js 函数,没有什么特别之处。
async /await 就是 Promise 的 then 调用
在 js 看来,上面的错误示范里等同于
const main = () => {
return new Promise((resolve, reject)=>{
setTimeout(function () {
return 'done';
}, 2000);
resolve();
});
};
const result = await main();
console.log(result);
所以不会有任何等待,直接返回了。
二、如何在普通函数里调用 async 函数
如果理解了上面的例子,那么这个问题也能解决了。
错误示范
async function test() {
return "it works";
}
function main() {
const result = await test();
console.log(result);
}
main();
将出现语法错误:
await test();
^^^^
SyntaxError: Unexpected identifier
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:599:28)
at Object.Module._extensions..js (module.js:646:10)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Function.Module.runMain (module.js:676:10)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:608:3
正确示范
async function test() {
return "it works";
}
function main() {
test()
.then((result)=>{
console.log(result);
});
}
main();
三、如何在普通函数里捕获 async 中的异常
Async 版本的异常捕获
async function test() {
throw new Error("500");
return "it works";
}
async function main() {
try{
const result = await test();
} catch (e) {
console.log(e.toString());
}
}
上面的例子中, main 函数捕获了 test 函数里抛出的错误。如果 main 函数不是一个 async 函数,应该怎么调用呢?再用 try/catch 就是错误的。
普通函数版本的捕获
async function test() {
throw new Error("500");
return "it works";
}
function main() {
test()
.then((result)=>{
console.log(result);
})
.catch((e)=>{
console.log(e.toString());
});
}
try/catch 一个 async 函数,本质就是 Promise 的 catch 调用
四、举一反三
怎样用 Async 方式调用 axios 库
如果上面的例子都能理解了,我们就能更遂心应手地编写 ES 代码了。比如想要在浏览器里使用 ajax 操作的 js 库:axios。 官方例子
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
它也没有提供 async 版本的写法,我们应该怎么改呢?
try {
const response = await axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
});
} catch (error) {
console.log(error);
}
怎么以 Async 方式调用 Element-UI 里的 confirm 消息
官方例子
export default {
methods: {
open2() {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
}
}
改成 Async 函数
export default {
methods: {
async open2() {
try {
await this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
this.$message({
type: 'success',
message: '删除成功!'
});
} catch (e) {
this.$message({
type: 'info',
message: '已取消删除'
});
}
}
}
}
想通这个问题后,很多写法霍然开朗,也明白了为什么 Promise 很好的解决了 callback hell 问题。
文章评论:做一头严肃的大叫驴
根据过去的经验得出,大多数评论是毫无意义的灌水,还有一小部分内容是针对文章的补充和纠错。如果你有建议请邮件联系。