关于JavaScript里的Async那些事儿
一、起源
JavaScript 编程中最困难的就是异步操作,所以一直有群技术大牛试图搬开这块巨石,以达到一劳永逸的效果。
向前追溯,从回调函数,到 Promise 对象,再到 Generator 函数,虽然能撬动这块巨石,但就是移不开或者说留
下一个大坑,让后人苦不堪言。由于它们抽象的底层机制和复杂性,都使得人们在这条路上寸步难行。
佛说:异步编程的最高境界,就是根本不用关心它是不是异步。
所以就算是异步I/O只是读取一个文件而已,但也要做到精益求精。
直到ES2017 标准引入了 async 函数,方才使异步操作变得更加方便。
二、我是谁?
我是async 函数,是 Generator 函数的语法糖,也就是他的简便方法。
用Generator 函数提取文件
var fs = require('fs');
var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
化身为async 函数,就是下面这样。
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
其实async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await。
三、async 函数的优点
async 函数对 Generator 函数的改进,体现在以下三点。
(1)内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。使其步入普通函数的行列,一行执行
var result = asyncReadFile();
(2)更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
(4)返回值是promise。这比Generator 返回函数是Iterator对象方便许多,直接可用then实现进一步操作,然后我们就会使用到then的语法糖await命令。
四、使用场景
原始的写法
使用promise方法
编辑切换为居中
添加图片注释,不超过 140 字(可选)
使用async和await方法
编辑切换为居中
添加图片注释,不超过 140 字(可选)
五、万一报错,那么...
注意:await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。
await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 报错
docs.forEach(function (doc) {
await db.post(doc);
});
}
上面代码会报错,因为 await 用在普通函数之中了。但是,如果将 forEach 方法的参数改成 async 函数,也有问题。
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 可能得到错误结果
docs.forEach(async function (doc) {
await db.post(doc);
});
}
上面代码可能不会正常工作,原因是这时三个 db.post 操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用 for 循环。
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}
如果确实希望多个请求并发执行,可以使用 Promise.all 方法。
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
// 或者使用下面的写法
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(results);
}
- End -

猜你喜欢LIKE
Java培训问答更多>>
新Java行业疑惑解答:Java的内存管理是如何工作的?
新java script是什么?为什么要学java script
新java和大数据哪个好?未来哪个职业发展更好
新java培训班多久能学会?培训周期大概多久
新java script和java的区别有哪些?如何区分
新java script的数据类型主要有哪些?怎样学的更快
新c语言与java区别在哪里?去培训机构学哪个比较好
Java面试题库 更多>>
华为外包java面试题-Java实现单链表的逆序
Java程序员面试题
Java面试题及答案
什么是线程的上下文切换?
如何撤销已经推送(push)到远端仓库的提交(commit)信息?
你了解哪些加密算法?
- 北京校区
- 大连校区
- 广州校区
- 成都校区
- 杭州校区
- 长沙校区
- 合肥校区
- 南京校区
- 上海校区
- 深圳校区
- 武汉校区
- 郑州校区
- 西安校区
- 青岛校区
- 重庆校区
- 太原校区
- 沈阳校区
- 南昌校区
- 哈尔滨校区
