async/await  其实就是Generator的语法糖。  如果对Generator还不太熟悉可以先看这篇 听说你还没听过 ES6 的 Generator 函数? 
async函数其实就相当于funciton *的作用 
await就相当与yield的作用。 
 
而在async/await机制中,自动包含了上述封装出来的spawn自动执行函数 。
 
async函数是ES6的新语法;使得异步操作变得更加方便。 使用关键字async来修饰,表示函数里面可能有异步操作,在函数内部使用await来表示异步。
async函数中如果没有await,那么和普通函数一样的 
一旦加了 await ;那么 await下面的代码就是异步的 ; 
 
微任务和宏任务 
微任务和宏任务: 这两个都是异步队列中的任务。
 
异步任务: setTimeout、setInterval、事件、promise的then、ajax、async await 
微任务:  promise的 then 、  async ****await 、  process.nextTick 
宏任务 :  setTimeout 、  setInterval 、  ajax ;  
 
执行顺序:  先执行同步任务,再执行异步任务;先执行微任务,再执行宏任务; 
async函数基本使用 
async函数是 Generator 函数的语法糖。async声明该函数是异步的,且该函数会返回一个promise。 
 
语法规则: 
async 是 function 的一个前缀,只有  async 函数中才能使用  await 语法  
async 函数是一个 Promise 对象,有无  resolve 取决于有无在函数中  return 值  
await 后面跟的是一个 Promise 对象,如果不是,则会包裹一层  Promise.resolve() 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 function  fn1 ( ) {	console .log (200 ); } async  function  fn ( ) {	await  fn1 ();  	console .log (100 );  } console .log (fn ()); fn ().then (function (a ){	console .log (a);  }) 
 
 
async 返回一个promise的实例;  默认是成功态 ;async函数内部  return语句返回的值  ,会成为  then方法回调函数的参数  。 
1 2 3 4 5 6 7 8 async  function  fn ( ) {	return  1 ; } console .log (fn ());fn ().then (function  (a ) { 	console .log (a);  }); 
 
 
下面这个例子中:fn1函数返回的是一个promise,fn函数中,只有当 await后面的执行成功,也就是 promise的状态变成 Fulfilled (  resolve(参数) )了之后才会把 await后面的代码放入微任务队列里面  ,整个 async函数结束,继续执行后面的代码。  而且 await的返回值就是 resolve(参数) 中的参数  。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  fn1 ( ) {	return  new  Promise (function  (resolve,reject ) { 		setTimeout (function  ( ) { 			console .log (300 ); 			resolve () 		},200 ) 	}) } async   function  fn ( ) {	await  fn1 (); 	 	console .log (200 );  } fn ().then (function  ( ) {}).then (function  ( ) { }) 
 
async 函数返回的 Promise 对象,必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变 也就是说,只有当 async 函数内部的异步操作都执行完,才会执行 then 方法的回调 。
async函数的多种形式
函数声明 :async function fn(){} 
函数表达式 :let fn = async function(){} 
箭头函数 :let fn = async () => {} 
对象的方法 :let obj = { async fn(){} }; obj.fn().then(...) 
class的方法 : 
 
1 2 3 4 5 6 7 8 9 10 11 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 (…); 
 
async函数的错误处理
如果 async 函数内部抛出异常,则会导致返回的 Promise 对象状态变为 reject 状态。抛出的错误而会被 catch 方法回调函数接收到。 
如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。 防止出错的方法,也是将其放在 try...catch代码块之中 。 
 
await的多种类型await+Promise这是最常见的场景。
await 会等待Promise的状态改为fullfilled:
如果成功,那么会  将 async函数剩余任务(也就是 await后面的代码)推入到微任务队列  
如果失败,那么剩余任务不会被推入微任务队列执行,它会  返回 Promise.reject(err) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 async  function  async1 ( ) {                    console .log ('async1 start' )     await  async2 ()     console .log ('async1 end' ) } async  function  async2 ( ) {    console .log ('async2 start' )     return  new  Promise ((resolve, reject ) =>  {         resolve ()            console .log ('async2 promise' )                   }) } console .log ('script start' )setTimeout (function  ( ) {      console .log ('setTimeout' ) }, 0 ) async1 ()new  Promise (function  (resolve ) {    console .log ('promise1' )     resolve ()  })     .then (function  ( ) {         console .log ('promise2' )     })     .then (function  ( ) {         console .log ('promise3' )     }) console .log ('script end' ) 
 
await+普通值
即使await右边非函数,只是一个普通的数值,但它本质上是将其转化为  Promise.resolve(普通值) ,所以会返回一个成功的 promise 。 
因此 ,  当await等待到了成功的结果后,它会将async函数剩余内容(也就是 await后面的代码)推入到微任务队列中等待执行  。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  async  function  run ( ) {     console .log ('start 1' )     const  res = await  2                 console .log (res)     console .log ('end' ) } run ()console .log ('3' )
 
await+函数 
如果await 右边是一个函数,它会立刻执行这个函数,而且只有当这个函数执行结束后(即函数完成)!才会将 async剩余任务(也就是 await后面的代码)推入微任务队列  。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function  fn ( ) {    console .log ('fn start' )     console .log ('fn end' ) } async  function  run ( ) {    console .log ('start 1' )     const  res = await  fn ()      console .log (res)     console .log ('end' ) } run ()console .log ('3' )
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 async  function  async1 ( ) {    console .log (1 )     await  async2 ()     console .log (2 ) } const  async2  = async  ( ) => {    await  (async  () => {           await  (() =>  {		             console .log (3 )          })()         console .log (4 )                     })() } const  async3  = async  ( ) => {    Promise .resolve ().then (() =>  {         console .log (6 )       }) } async1 ()console .log (7 )async3 ()
 
如果await后面的返回的promise状态变成 rejected ,那么它  将不会再把剩余任务推入到微任务队列,跳过整个 async函数继续执行后面的代码,并在执行完之后 Uncaught : 
await+定时器(函数)
定时器setTimeOut()和setInterval()也都是函数,不过与普通函数不同的是,定时器函数返回的是一个  定时器ID 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 async  function  async1 ( ) {    console .log (1 )     await  async2 ()     console .log (2 ) } const  async2  = async  ( ) => {    await  setTimeout ((_ ) =>  {         Promise .resolve ().then ((_ ) =>  {             console .log (3 )         })         console .log (4 )     }, 0 )                } const  async3  = async  ( ) => {            Promise .resolve ().then (() =>  {         console .log (6 )     })                   } async1 ()console .log (7 )async3 ()
 
为什么要使用async/await? 都已经有Promise了,为什么还要使用async/await?
可以隐藏 Promise ,更易于理解 假设我们想请求一个接口,然后把响应的数据打印出来,并且捕获异常。
1 2 3 4 5 6 7 8 9 function  logFetch (url ) {  return  fetch (url)     .then (response  =>  response.text ())     .then (text  =>  {       console .log (text);     }).catch (err  =>  {       console .error ('fetch failed' , err);     }); } 
 
1 2 3 4 5 6 7 8 9 async  function  logFetch (url ) {  try  {     const  response = await  fetch (url);     console .log (await  response.text ());   }   catch  (err) {     console .log ('fetch failed' , err);   } } 
 
虽然代码的行数差不多,但是代码看起来更加简洁,少了很多  then 的嵌套 。请求一个接口数据,然后打印,就像你看到的,很简单。
用同步的思路写异步逻辑 想获取一个网络资源的大小:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function  getResponseSize (url ) {  return  fetch (url).then (response  =>  {     const  reader = response.body .getReader ();     let  total = 0 ;     return  reader.read ().then (function  processResult (result ) {       if  (result.done ) return  total;       const  value = result.value ;       total += value.length ;       console .log ('Received chunk' , value);       return  reader.read ().then (processResult);     })   }); } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const  processResult  = (result ) =>{      if  (result.done ) return  total;       const  value = result.value ;       total += value.length ;       console .log ('Received chunk' , value);       return  reader.read ().then (processResult); } function  getResponseSize (url ) {  return  fetch (url).then (response  =>  {     const  reader = response.body .getReader ();     let  total = 0 ;     return  reader.read ().then (processResult)   }); } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async  function  getResponseSize (url ) {  const  response = await  fetch (url);   const  reader = response.body .getReader ();   let  result = await  reader.read ();   let  total = 0 ;   while  (!result.done ) {     const  value = result.value ;     total += value.length ;     console .log ('Received chunk' , value);          result = await  reader.read ();   }   return  total; } 
 
因为 await 表达式会阻塞运行,甚至可以直接阻塞循环,所以整体看起来像同步的代码,也更符合直觉,更容易读懂这个代码。
解决了Promise参数传递麻烦的弊端 假设一个业务,分多个步骤完成,每个步骤都是异步的而且依赖于上一个步骤的结果,并且每一个步骤都需要之前每个步骤的结果。。用 setTimeout 来模拟异步操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21    function  takeLongTime (n ){  return  new  Promise ((resolve ) =>  {     setTimeout (() =>  resolve (n + 200 ),n);   }) } function  step1 (n ){  console .log (`step1 with ${n} ` );   return  takeLongTime (n); } function  step2 (m,n ){  console .log (`step2 with ${m}  + ${n} ` );   return  takeLongTime (m + n); } function  step3 (k,m,n ){  console .log (`step3 with ${k}  + ${m}  + ${n} ` );   return  takeLongTime (k + m + n); } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function  doIt ( ) {  console .time ('doIt' );   let  time1 = 300 ;   step1 (time1)     .then ((time2 ) =>  {       return  step2 (time1,time2)           .then ((time3 ) =>  [time1,time2,time3])     })     .then ((times ) =>  {       let  [time1,time2,time3] = times;       return  step3 (time1,time2,time3)     })     .then ((result ) =>  {       console .log (`result is ${result} ` );       console .timeEnd ('doIt' );     }) } doIt ();
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 async  function  doIt ( ) {  console .time ('doIt' );   let  time1 = 300 ;   let  time2 = await  step1 (time1);   let  time3 = await  step2 (time2,time1);   let  result = await  step3 (time3,time2,time1);   console .log (`result is ${result} ` );   console .timeEnd ('doIt' ); } doIt ();
 
一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了,而async/await则解决了Promise参数传递麻烦的弊端
async/await的错误捕获 
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好  把  await 命令放在  try...catch 代码块中,这样就不会影响后面代码的运行 。
因为如果await后面的返回的promise状态变成rejected,那么它将不会再把剩余任务推入到微任务队列,剩余的代码也就不会再执行了。(await不能提取reject的结果) 
 
1 2 3 4 5 6 7 8 9 const  fn  = async  ( )=> {  console .log ('我在await Promise之前' );   const  result = await  Promise .reject ('我是错误信息' );   console .log (result);   console .log ('我在await Promise之后' ); } fn ()console .log ('我在fn()之后' )
 
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const  fn  = async  ( )=> {  console .log ('我在await Promise之前' );   try {     const  result = await  Promise .reject ('我是错误信息' );     console .log (result);   }catch (e){     console .log ('error:' , e)   }finally  {     console .log ('我在finally里面,始终会执行' );      }   console .log ('我在await Promise之后,我没有受到影响' ); } fn ()console .log ('我在fn()之后' )
 
运行结果:
可以看到虽然reject了,但仍console.log('我在await Promise之后,我没有受到影响');,说明不影响后面的代码执行。
小心await阻塞 
由于 await 能够阻塞 async 函数的运行,所以代码看起来更像同步的代码,更容易阅读和理解。但是要小心 await 阻塞,因为有些阻塞是不必要的,不恰当使用可能会影响代码的性能。
 
假如要把一个网络数据和本地数据合并,错误的实例可能是这样子:
1 2 3 4 5 async  function  combineData (url, file ) {  let  networkData = await  fetch (url)   let  fileData = await  readeFile (file)   console .log (networkData + fileData) } 
 
其实我们不用等一个文件读完了,再去读下个文件,我们可以两个文件一起读,读完之后再进行合并,这样能提高代码的运行速度。我们可以这样写:
1 2 3 4 5 6 7 async  function  combineData (url, file ) {  let  fetchPromise = fetch (url)   let  readFilePromise = readFile (file)   let  networkData = await  fetchPromise   let  fileData = await  readFilePromise   console .log (networkData + fileData) } 
 
这样的话,就可以同时 网络请求 和 读取文件 了,可以节省很多时间。这里主要是利用了 Promise 一旦创建就立刻执行的特点——fetchPromise 和  readFilePromise 是两个异步操作的 Promise 对象,它们被创建后立即开始执行(一起执行的),而不是顺序执行 。
可以直接使用 Promise.all 的方式来处理,或者 await 后面跟 Promise.all
1 2 3 4 5 async  function  combineData (url, file ) {  let  promises = [fetch (url), readFile (file)]   let  [networkData, fileData] = await  Promise .all (promises)   console .log (networkData + fileData) } 
 
async/await的实现原理 下面的代码实现了async/await的部分功能,yield相当于await,但是我们发现代码中存在了多次的嵌套调用,这还取决于 yield 的数量,这明显是不能容忍的,与此同时,gen 最终返回的也不是一个 Promise 对象,因此我们可以通过一个高阶函数来解决问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function  p (num ) {  return  Promise .resolve (num * 2 ) } function * generator ( ) {  const  value1 = yield  p (1 )   const  value2 = yield  p (value1)   return  value2 } const  gen = generator ();const  next1 = gen.next ()next1.value .then ((res1 ) =>  {   console .log (res1)   const  next2 = gen.next (res1)   next2.value .then ((res2 ) =>  {     console .log (res2)   }) }) 
 
所谓高阶函数,就是在函数中返回函数。
因为async函数是一个可以返回Promise的函数,所以可以在高阶函数中返回一个返回值为 Promise 对象的函数:(同时处理一下嵌套调用问题=>改成递归调用 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 function  p (num ) {  return  Promise .resolve (num * 2 ) } function * generator ( ) {  const  value1 = yield  p (1 )   const  value2 = yield  p (value1)   return  value2 } function  higherOrderFn (generatorFn ) {  return  () =>  {     return  new  Promise ((resolve, reject ) =>  {       let  gen = generatorFn ()              const  doYield  = (val )=>{         console .log (val)         let  res         try {           res = gen.next (val)         }catch (err){             reject (err)         }         const  {value,done} = res                  if (done){           return  resolve (value)         }else {                      value.then ((val )=> {doYield (val)})         }       }       doYield ()     })   } } const  asyncFn = higherOrderFn (generator)()
 
至此,generator 的函数体已经能和 async 函数实现契合了。