目录
什么是aysnc和await
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
为了使我们的异步代码,更像同步的代码
aysnc函数解决了什么
在async/await之前,我们有三种方式写异步代码
-
嵌套回调
-
以Promise为主的链式回调
-
使用Generators
但是,这三种写起来都不够优雅,ES7做了优化改进,async/await应运而生,async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
async 函数的优点
async 函数对 Generator 函数的改进,体现在以下三点。
(1)内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
var result = asyncReadFile();
(2)更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
怎么使用async函数
async函数语法
- 自动将常规函数转换成Promise,返回值也是一个Promise对象
- 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
- 异步函数内部可以使用await
async function name([param[, param[, ... param]]]) { statements }
name: 函数名称。
param: 要传递给函数的参数的名称
statements: 函数体语句。
返回值: 返回的Promise对象会以async function的返回值进行解析,或者以该函数抛出的异常进行回绝。
await语法
-
await 放置在Promise调用之前,await 强制后面的代码等待,直到Promise对象resolve,得到resolve的值作为await表达式的运算结果
-
await只能在async函数内部使用,用在普通函数里就会报错
[return_value] = await expression;
expression: 一个 Promise 对象或者任何要等待的值。
返回值:返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
错误处理
在async函数里,无论是Promise reject的数据还是逻辑报错,都会被默默吞掉,所以最好把await放入try{}catch{}中,catch能够捕捉到Promise对象rejected的数据或者抛出的异常
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模拟出错,返回error
});
}
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms); //这里返回了错误
console.log('end'); //所以这句代码不会被执行了
} catch(err) {
console.log(err); //这里捕捉到错误error
}
}
asyncPrint(1000);
如果不用try/catch的话,也可以像下面这样处理错误(因为async函数执行后返回一个promise)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模拟出错,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms)
console.log('end'); //这句代码不会被执行了
}
asyncPrint(1000).catch(err => {
console.log(err); // 从这里捕捉到错误
});
如果你不想让错误中断后面代码的执行,可以提前截留住错误,像下面
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, ms); //reject模拟出错,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms).catch(err => { // 注意要用catch
console.log(err)
})
console.log('end'); //这句代码会被执行
}
asyncPrint(1000);
async+await基本结构
function func(a, b) {
return a + b
}
let ret = func(10, 30);
let ret1 = func(50, 320);
let ret2 = func(1560, 30);
let ret3 = func(10, 3560);
console.log(ret + ret1 + ret2 + ret3);
如果Promise对象也能有对应的方式来接收,写成类似:
let data1 = await readfilePromise; // 直接获取成功的数据
async的最终格式如下:
async function func() {
let data1 = await promise对象1;
let data2 = await promise对象2;
let data3 = await promise对象3;
}
// 相当于让异步函数对象1先执行完毕之后,再执行异步函数对象2,再执行异步函数对象3
注意事项
-
如果await后面只写一个基本数据类型,会这个基本数据类型进行包装,包装成一个 Promise 对象
async function func() { let data1 = await 123; //1. await后面只写一个基本数据类型 会这个基本数据类型进行包装,包装成一个 Promise 对象 // 即data1相当于: new Promise((resolve,reject)=>{resolve(123)}) console.log("data1:", data1); //data1: 123 return data1 // return await data1 } let a = func(); a.then((data)=>{ console.log(data); //123 接收到上面的返回值Promise对象的执行结果 });
-
如果await后面是一个 Promise,会把 resolve 的值返回
-
async 函数里面的 await 是异步的,不是同步
async function func() { console.log("start-------"); let data1 = await readFile(filePath1, "utf-8"); console.log("end-----------"); let data2 = await readFile(filePath2, "utf-8"); let data3 = await readFile(filePath3, "utf-8"); console.log(data1+data2+data3); } console.log("start"); func(); console.log("end"); //输出结果依次为: //start //start------- //end //end-----------