async函数
含义
他就是Generator函数的语法糖,generator函数读取两个文件
const fs=require('fs');
const readFile=function(fileName){
return new Promise(function(resolve,reject){
fs.readFile(fileName,function(error,data){
if(error) return reject(error);
resolve(data);
});
});
};
const gen=function* (){
const f1=yield readFile('/etc/fstab');
const f2=yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}
上面的代码gen写成async就是这样
const asyncReadFile=async function (){
const f1=await readFile('/etc/fstab');
const f2=await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}
async对Generator函数的改进,体现在一下四点
- 内置执行器 执行与普通函数一样
- 更好的语义
- 更广的适用性
- 返回值是Promise
基本用法
async function getStockPriceByName(name){
const symbol=await getStockSymbol(name);
const stockPrice=await getStockPrice(symbol);
return stockPrice;
}//返回一个promise
getStockPriceByName('goog').then(function(result){
console.log(result);
});
另一个例子
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms);
});
}
async function asyncPrint(value,ms){
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
上面代码50ms输出helloworld
由于saync函数返回的是Promise对象,可以作为await命令的参数,所以,上面的例子也可以写成下面形式
async function timeout(ms){
await new Promise((resolve)=>{
setTimeout(resolve,ms);
});
}
async function asyncPrint(value,ms){
await timeout(ms);
console.log(value);
}
asyncPrint('hello world',50);
async函数有多种使用形式
//函数声明
async function foo(){}
//函数表达式
const foo = async function() {};
//对象的方法
let obj={ async foo(){} };
obj.foo().then(...)
//Class的方法
class Storage{
constructor(){
this.cachePromise = caches.open('avatars');
}
async getAvatar(name){
const cache=await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage=new Storage();
storage.getAvatar('jake').then(...);
//箭头函数
const foo = async ()=>{};
语法
async函数语法总体比较简单,难点是错误处理机制
返回Promise对象
函数内的返回值成为then方法回调函数的参数。
async function f(){
return "hello world";
}
f().then(v => console.log(v));//hello world
async函数内部抛出的错误会导致返回的Promise对象变为reject状态,抛出的错误对象会被catch方法回调函数接收
async function f(){
throw new Error('出错了');
}
f().then(
v=>console.log(v),
e=>console.log(e)
)
//Error: 出错了
Promise对象的状态变化
async函数返回的Promise对象,必须等await命令后面的Promise对象执行完,才会发生状态改变。
async function getTitle(url){
let response=await fetch(url);
let html=await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle("xxxxxx").then(console.log);
上述getTitle内的操作全部完成,才会执行console.log
await命令
await命令后面是一个Promise对象,返回该对象的结果,如果不是Promise对象返回对应的值。
async function f(){
return await 123;
}
f().then(v => console.log(v));
另一种情况返回thenable对象,await会将其等同Promise对象
class Sleep{
constructor(timeout){
this.timeout=timeout;
}
then(resolve,rejcet){
const startTime=Date.now();
setTimeout(
()=>resolve(Date.now()-startTime),
this.timeout;
);
}
}
(async ()=>{
const sleepTime = await new Sleep(1000);
console.log(sleepTime);
})
上面代码await命令后面是Sleep对象实例,定义了then,await将其视为Promise处理。
实现简单休眠
function sleep(interval){
return new Promise(resolve=>{
setTimeout(resolve,interval);
})
}
//用法
async function one2FiveInAsync(){
for(let i=1;i<=5;i++){
console.log(i);
await sleep(1000);
}
}
one2FiveInAsync();
await命令后面Promise对象如果变为reject状态,则reject参数会被catch方法回调函数接收
async function f(){
await Promise.reject("出错了");
}
f()
.then(v=>console.log(v))
.catch(e=>console.log(e));
//出错了
注意上面代码await没有return,但是reject方法的参数依然传入了catch方法的回调函数。
任何一个await语句后面的Promise对象变为reject状态,整个async函数都会中断。
async function f(){
await Promise.reject('出错了');
await Promise.resolve('hello world');//不会执行
}
第二行代码不会执行,因为await语句状态变成了reject
如果需要即使前面一个异步操作失败,也不要中断后面的异步操作,可以将await放在try…catch结构里面。
async function f(){
try{
await Promise.reject('出错了');
}catch(e){
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
//hello world
另一种方法是await后面的Promise对象再跟一个catch方法,处理前面可能出现的错误。
async function f(){
await Promise.reject('出错了')
.catch(e => console.log(e));
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v));
//出错了
//hello world
错误处理
如果await后面的异步操作出错,等同于async函数返回的Promise对象被reject
async function f(){
await new Promise(function(resolve,reject){
throw new Error('出错了');
});
}
f()
.then(v => console.log(v))
.catch(e => console.log(e));
上面代码async函数f执行后,await后面的Promise对象抛出一个错误对象,导致catch方法回调函数被调用,参数为抛出的错误对象
防止出错的方法,也可以将其放在try…catch中
async function f(){
try{
await new Promise(function(resolve,reject){
throw new Error('出错了');
});
}catch(e){
}
return await('hello world');
}
如果有多个await命令,可以统一放在try…catch中
async function main(){
try{
const val1=await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}catch (err) {
console.error(err);
}
}
下面例子使用try…catch结构 实现多次重复尝试
const superagent = require('superagent');
const NUM_RETRIES = 3;
async function test() {
let i;
for (i = 0; i < NUM_RETRIES; ++i) {
try {
await superagent.get('http://google.com/this-throws-an-error');
break;
} catch(err) {}
}
console.log(i); // 3
}
test();
上面代码,成功跳出,失败catch捕捉,下一轮
使用注意点
同时触发
let [foo,bar]=await Promise.all([getFoo(),getBar()]);//1
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;//2
并发执行
async function dbFuc(db){
let docs=[{},{},{}];
let promises=docs.map((doc)=>db.post())
let results=await Promise.all(promises);
console.log(results);
}
async函数保留运行栈
const a = async()=>{
await b();
c();
}
async 函数实现原理
将Generator和自执行器包装在一个函数里
async function fn(args){
}
//等同于
function fn(args){
return spawn(function* (){
})
}
具体实现
function spawn(genF){
return new Promise(function(resolve,reject){
const gen=genF();
function step(nextF){
let next;
try{
next=nextF();
}catch(e){
return reject(e);
}
if(next.done){
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v){
step(function(){ return gen.next(v); })
},function(e){
step(function(){ return gen.throw(e); })
});
}
step(function(){ return gen.next(undefined); });
})
}
与其他异步比较
并发远程请求
async function logInOrder(urls){
const textPromises=urls.map(async url->{
const response = await fetch(url);
return response.text();
});
for(const textPromise of textPromises){
console.log(await textPromise);
}
}