第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

ES6+ async/await

1. 前言

前面幾節(jié)我們已經(jīng)學(xué)習(xí)了解決異步的方案 Promise 和 Generator,上一節(jié)我們也通過一個案例使用 Promise + Generator 實現(xiàn)了一個比較好的異步解決方案。同時我們實現(xiàn)了一個簡版的 co 庫,讓我們在使用 Generator 函數(shù)處理異步任務(wù)時更加方便,但這不是最完美的解決方案。

本節(jié)我們將學(xué)習(xí) ES7 推出的 async/await 其特性是對 JS 的異步編程進行了重要的改進,在不阻塞主線程的情況下,它給我們提供了使用同步代碼的風(fēng)格來編寫異步任務(wù)的能力。另外,我們要明確的是 async/await 其實是 Promise + Generator 的語法糖,為了幫助我們像寫同步代碼一樣書寫異步代碼,代碼風(fēng)格更優(yōu)雅,錯誤捕獲也更容易。

本節(jié)我們將通過對上一節(jié)案例的改造。在不需要 co 庫的情況下直接使用 async/await 讓我們更加深刻地理解異步方案的演變過程。

2. 改造上節(jié)案例

上一節(jié) 我們通過一個案例來講解 Promise + Generator 在實際應(yīng)用中的使用,通過 Generator 函數(shù) 和 yield 讓異步代碼看起來像同步代碼一樣執(zhí)行。但是這樣里面存在的一個問題就是生成器函數(shù)直接執(zhí)行,需要手動處理。為了解決深層回調(diào)的問題我們借助了 co 庫來幫助我們?nèi)?zhí)行生成器函數(shù),從而解決了回調(diào)地獄的問題。下面是上一節(jié)的代碼。

const ajax = function(api) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (api === 'api_1') {
        resolve('api_2');
      }
      if (api === 'api_2') {
        resolve(100);
      }
    }, 0)
  })
}

function * getValue() {
  const api = yield ajax('api_1');
  const value = yield ajax(api);
  return value;
}

co(getValue()).then(res => {
  console.log(res);
})

上面的代碼中 getValue 是生成器函數(shù),不能直接調(diào)用,這里用 co 庫來進行執(zhí)行,然后通過 Promise 的鏈式調(diào)用獲取執(zhí)行后的結(jié)果。但是這里借助了 co 的庫,我們其實最希望的是能像執(zhí)行普通函數(shù)一樣直接調(diào)用 getValue 就能執(zhí)行并得到結(jié)果。 async/await 的出現(xiàn)就是為了抹平在調(diào)用時所做的額外步驟。那讓我們看看 async/await 是怎么用的:

async function getValue() {
  const api = await ajax('api_1');
  const value = await ajax(api);
  console.log(value)
  return value;
}
getValue()	// 控制臺打印 value的值是:100

上面的代碼中我們可以看出使用 async/await 定義的 getValue 函數(shù)和生成器函數(shù) */yield 定義的基本相同,但是在執(zhí)行時 async/await 定義的函數(shù)直接調(diào)用即可。從這里我們就能看到 async/await 的優(yōu)點,無需過多的操作非常優(yōu)雅和簡潔。

3. 用法

上面我們基本了解了 async 函數(shù),下面我們就來看看它的基本使用和需要注意的地方。

定義一個異步函數(shù)時需要使用 asyncfunction 關(guān)鍵字一起來完成,類似生成器函數(shù)中的 yield 來暫停異步任務(wù),在 async 函數(shù)中使用 await 關(guān)鍵去等待異步任務(wù)返回的結(jié)果。

async 函數(shù)其本質(zhì)是 Promise + Generator 函數(shù)組成的語法糖,它為了減少了 Promise 的鏈式調(diào)用,解放了 Generator 函數(shù)的單步執(zhí)行。主要語法如下:

async function name([param[, param[, ... param]]]) { 
    statements 
}

上面代碼中的 statements 是函數(shù)主體的表達式,async 函數(shù)可以通過 return 來返回一個值,這個返回值會被包裝成一個 Promise 實例,可以被鏈式調(diào)用。下面我們來看兩段等價代碼。

// 下面兩段代碼時相同的
async function foo() {
   return 100
}
function foo() {
   return Promise.resolve(100)
}

// 下面兩段代碼時相同的
async function foo() {
  await 1;
}
function foo() {
   return Promise.resolve(1).then(() => undefined)
}

上面的兩段代碼效果時相同的,這里我們就不去探究 async 函數(shù)是怎么實現(xiàn)的,其大概原理類似上節(jié)寫的 co 庫,有興趣的小伙伴可以去 babel 上去看看 async 函數(shù)編譯是什么樣子的。

當(dāng)在 async 函數(shù)中返回的是一個普通值或 await 后跟一個普通值時,此時的 async 函數(shù)是同步的。在 Promise 中失敗是不能被 try...catch 捕獲的,需要通過 catch 的方式來捕獲錯誤。而使用 async 函數(shù)則是可以通過 try...catch 來捕獲。

async function foo() {
	return new Error('Throw an error');
}

foo().then(res => {
  console.log(res)
}).catch(err => {
  console.error(err)	// Error: Throw an error
})

async function foo2() {
  try{
    var v = await foo()
    console.log(v)
  } catch(e) {
    console.log(e);	// Error: Throw an error
  }
}
foo2()

上面的代碼中在執(zhí)行 foo() 直接拋出了一個錯誤,而 Promise 和 async/await 對錯誤的捕獲是不同的,我們知道 Promise 是通過 then 中的失敗回調(diào)和 catch來捕獲錯誤的,而 async 函數(shù)使用的是 try...catch 更像同步的方式。

3.1 錯誤捕獲

但是有個問題,當(dāng)程序需要同時處理多個異步任務(wù)時,那我們使用 async/await 怎樣捕獲那個異步任務(wù)出現(xiàn)錯誤呢?try 塊中的代碼只要程序出現(xiàn)錯誤就會拋出錯誤,但是不知道是哪個異步任務(wù)出錯了不利于定位問題。如果使用多個 try...catch :

const task = function (num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (num === 300) {
        reject('throw error')
      } else {
       	resolve('imooc'); 
      }
    }, 1000)
  })
}

async function foo() {
  try {
    let res1 = await task(100);
    try {
      let res2 = await task(200);
      try {
        let res3 = await task(300);
      } catch(e) {
        console.log('res3', e)
      }
    } catch(e) {
      console.log('res2', e)
    }
  } catch(e) {
    console.log('res1', e)
  }
}
foo()	// res3 throw error

看到上面的代碼你是不是覺得很難受啊,又回到了嵌套地獄的原始問題了。async 函數(shù)在異常捕獲時,沒有非常完美的解決方案,這主要源自依賴 try...catch 對錯誤的捕獲。但有一些還算比較優(yōu)雅的解決方案,我們已經(jīng)知道了 async 函數(shù)返回的是一個 Promise 那么我們是不是可以使用 Promise 的 catch 來捕獲呢?答案是當(dāng)然的呢。

async function foo() {
  let res1 = await task(100).catch(err => console.log('res1', err));
  let res2 = await task(200).catch(err => console.log('res2', err));
  let res3 = await task(300).catch(err => console.log('res3', err));
}
foo()	// res3 throw error

上面的代碼看起來就比嵌套的 try...catch 感覺好很多,這也是一個比較好的解決方式。在使用 catch 時需要弄清楚 Promise 和 async 函數(shù)之間的關(guān)系,不然就很難理解這種寫法。

3.2 濫用 async/await

既然 async/await 這么優(yōu)雅簡潔,那在編程的過程中都使用這個就好啦!其實這里是一個坑,很多時候 async/await 都會被濫用導(dǎo)致程序卡頓,執(zhí)行時間過長。

async function foo() {
  let res1 = await task(100);
  let res2 = await task(200);
  let res3 = await task(300);

  return { res1, res2, res3 }
}
foo()

在很多時候我們會寫成這樣的代碼,如果后一個任務(wù)依賴前一個任務(wù)這樣寫完全沒問題,但是如果是三個獨立的異步任務(wù),那這樣寫就會導(dǎo)致程序執(zhí)行時間加長。這樣的代碼過于同步化,我們需要牢記的是 await 看起來是同步的,但它仍然屬于異步的內(nèi)容,最終還是走的回調(diào),只是語言底層給我們做了很多工作。

針對沒有關(guān)聯(lián)的異步任務(wù)我們需要把它們解開,

const task = function (num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
       	resolve('imooc ' + num); 
    }, 1000)
  })
}

async function foo() {
  let res1Promes = task(100);
  let res2Promes = task(200);
  let res3Promes = task(300);

  let res1 = await res1Promes;
  let res2 = await res2Promes;
  let res3 = await res3Promes;

	console.log({ res1, res2, res3 })

  return { res1, res2, res3 }
}
foo();	// { res1: 'imooc 100', res2: 'imooc 200', res3: 'imooc 300' }

這里需要明白的一點是:為什么要把 task 拿到 await 外面去執(zhí)行呢?await 的本質(zhì)就是暫停異步任務(wù),等待返回結(jié)果,等到結(jié)果返回后就會繼續(xù)往下執(zhí)行。還要知道的是每個 task 都是一個異步任務(wù),像之前的那種寫法,await 會等待上一個異步任務(wù)完成才會走下一個。而我們把 task 拿出來了,也就是每個 task 會按照異步的方式去執(zhí)行。這個時候三個 task 都已經(jīng)開始執(zhí)行了,當(dāng)遇到 await 就只需要等到任務(wù)完成就行。所需要的時間是異步任務(wù)中耗時最長的,而不是之前的總和。

4. 小結(jié)

本節(jié)我們主要通過延續(xù)上一節(jié)的案例,用 async 函數(shù)給出了最優(yōu)的解決方案,從而完善了整個異步演變的過程,讓我們更加清晰地理解為什么會有 Promise?為什么會有生成器?為什么會有 async/await?由淺入深層層遞進地講解了 ES6 以后對異步任務(wù)處理的演變。然后我們主要學(xué)習(xí)了 async 函數(shù)的基本使用和錯誤處理的捕獲。最后,我們講解了如果不濫用 async 函數(shù)的案例,讓我們在以后寫程序的過程中更加得心應(yīng)手。