理解async函数就要先理解generator (生成器)函数,因为async是generator函数的语法糖。
Generator函数
Generator 函数是 ES6 提供的一种异步编程解决方案,可以先理解为一个状态机,封装了多个内部状态,执行Generator函数返回一个遍历器对象,通过遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
语法上,Generator 函数是一个普通函数,但是有两个特征。
- function关键字与函数名之间有一个*
- 函数内部使用yield表达式,定义不同的内部状态。
function* myGenerator() {
yield 'Hello'
yield 'world'
return 'ending'
}
let MG = myGenerator()
console.log(MG.next());
console.log(MG.next());
console.log(MG.next());
console.log(MG.next());
运行结果:
1)yield 表达式,可以在生成器函数内部暂停代码的执行,使其挂起,此时生成器函数仍然是运行并且是活跃的,其内部资源都会保留下来,只不过是处在暂停状态。
2)next(),在迭代器上调用next()方法,可以使代码从暂停的位置开始继续往下执行。
async/await
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)
})
})
}
//generator函数实现
const gen=function* (){
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}
//async实现
const gen1 = async function(){
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString()) ;
}
由此可见,async和generator在语法上是类似的。
async做一件什么事情?
带async关键字的函数,它使得你的函数的返回值必定是promise对象
1)如果async关键字函数返回的不是promise,会自动用Promise.resolve()包装
2)如果async关键字函数显式地返回promise,那就以你返回的promise为准
async function fun1(){
return 'hello'
}
function fun2(){
return 'async'
}
console.log(fun1());
console.log(fun2());
await在等什么?
await等的是右侧「表达式」的结果右侧是指await下边的语句(代码)
async function async1(){
console.log('async1 start');
await async2()
console.log('async1 end');
}
async function async2(){
console.log('async2');
}
console.log('script start');
async1()
await等到之后,做了一件什么事情?
右侧表达式的结果,就是await要等的东西。
等到之后,对于await来说,分为2种情况
1)不是promise对象
2)是promise对象
如果不是 promise , await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为 await表达式的结果
如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
});
console.log("script end");
async和promise的区别
1)Async/Await 代码看起来简洁一些,使得异步代码看起来像同步代码
2)async await与Promise一样,是非阻塞的。
3)async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。
调试:
promise
- promise不能在返回表达式的箭头函数中设置断点
- 如果你在.then代码块中设置断点,使用Step Over快捷键,调试器不会跳到下一个.then,因为它只会跳过异步代码
async
- async/await能够使得代码调试更简单
- 使用await/async时,你不再需要那么多箭头函数,这样你就可以像调试同步代码一样跳过await语句
- 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
- 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
promise异步:
ajax('aaa',()=>{
ajax('bbb',()=>{
ajax('ccc',()=>{
})
})
})
ajax('aaa').then(res=>{
return ajax('bbb')
}).then(res=>{
return ajax('ccc')
})
async异步:
async function fetch(){
await ajax('aaa')
await ajax('bbb')
await ajax('ccc')
}