首先看一下下列代码
var gen = function* (){
var a = yield readFile();
console.log(a);
var b= yield readFile();
console.log(b);
}
接下来用async形式来表示generator函数
var gen = async function(){
var a = await readFIle();
console.log(b);
var b = await readFile();
console.log(b);
}
从以上可以看出async函数就是在generator函数上改进的,就是把*改为async,然后把yield改为await,但是他在generator函数上有一些改进
哪些改进呢?
- 内置执行器
在我的另一篇博客中说出generator函数执行,需要为他写自动执行器,例如基于thunk函数的自动执行器,还有基于promise的自动执行器,还有就是co模块,但是async函数只要调用它就自己执行 - 更好的语义
- 更广泛的使用
co模块的yield语句后面必须是thunk函数或者是promise对象,但是await函数后面可以是原始类型的值 - 返回的是promise对象
可以使用then等
使用方法
async function getStockByName(name){
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockByName('sportShoe').then(function(result){
console.log(result);
})
我们可以看出只要我们调用一次getStockByName(),就自动执行,所以最后的result就是stockPrice
语法
返回Promise对象
调用async函数会返回一个promise对象,所以才可以调用then方法,then方法中的函数的参数就是async函数返回的值
const aw = async function(age){
var name = await search(age);
return name;
}
aw(20).then(name => console.log(name));
promise对象的状态变化
只有内部的所有异步过程结束后,才会调用then方法,或者抛出错误,或者遇到return语句
错误处理
我建议大家把所有的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);
}
}
注意的内容
let foo = await getFoo();
let bar = await getBar();
以上函数是独立的过程,被写成继发关系,就是顺序进行,这样会导致很费时,怎样会写成同时进行
有一下俩种写法
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
promise.all()方法就是将多个promise实例包装成一个
await命令必须写在async函数中写在别的函数中会出错
async函数的实现的原理
其实就是把generator函数写到一个具有自动执行代码的函数,然后在返回这个函数,和基于thunk函数的自动执行器基本一致,就不仔细分析async函数的源码了
顺序执行完一系列操作
promise的写法
function loginOrder(urls){
Const textPromises = urls.map(url => {
Return fetch(url).then(response => response.text());
})
TextPromise.reduce((chain, textPromise) => {
Return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
接下来看一下用async函数来表示上述操作
async function logInOrder(urls) {
for (const url of urls) {
const response = await fetch(url);
console.log(await response.text());
}
}
可以看出来用async表示非常简洁,但是这样会有一个问题所有提取网页都是继发,这样会很浪费时间,继发·就是提取网页是按照顺序进行的,所以我们现在要把它改成同时提取网页,代码如下
async function logInOrder(urls) {
// 并发读取远程URL
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// 按次序输出
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
因为在上述map函数中,匿名函数是async函数,所以await如果是在async函数中,那么这些await后面的操作都是同步进行的 ,这样就不会耗时了
异步遍历
我们都知道同步遍历器是部署在对象的Symbol.iterator的属性上的,但是异步遍历器是部署在对象的Symbol.asyncIterator属性上的
以下代码是一个异步遍历器的例子
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable.Symbol.asyncIterator();
asyncIterator
.next()
.then(iterResult1 => {
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
})
.then(iterResult2 => {
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
})
.then(iterResult3 => {
console.log(iterResult3); // { value: undefined, done: true }
});
从上面可以看出异步遍历器调用next方法返回一个promise,然后promise的状态变为resolve,然后调用then函数,执行then函数内的回调函数
由于调用next方法返回的是一个promise对象,所以说可以放在await命令后面,代码如下
async function f() {
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterableSymbol.asyncIterator;
console.log(await asyncIterator.next());
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
}
上面的代码接近于同步执行,但是我们想要所有的await基本上同时进行,可以有一下俩种表示
const asyncGenObj = createAsyncIterable(['a', 'b']);
const [{value: v1}, {value: v2}] = await Promise.all([
asyncGenObj.next(), asyncGenObj.next()
]);
console.log(v1, v2); // a b
//第二种
async function runner() {
const writer = openFile('someFile.txt');
writer.next('hello');
writer.next('world');
await writer.return();
}
runner();
for await of
我们都知道同步遍历器我们可以使用for of来遍历,但是异步的遍历器,我们使用for await of来遍历
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
异步generator函数
简单的说就是async与generator函数的结合,如下
async function* gen() {
yield 'hello';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
首先genObj.next()返回的是一个promise,然后调用then,执行then里面的函数,再看一个我认为比较重要的例子
async function* readLines(path) {
let file = await fileOpen(path);
try {
while (!file.EOF) {
yield await file.readLine();
}
} finally {
await file.close();
}
}
我们可以看出异步generator函数,既有await也有yield,其中await命令用于将file.readLine()返回的结果输入到函数内,然后yield用于将输入到函数内的结果从函数输出
在异步generator函数中的yield* 语句后面还可以跟一个异步的generator函数