中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript Async函數相關知識點有哪些

發布時間:2021-11-11 11:31:12 來源:億速云 閱讀:130 作者:iii 欄目:web開發

本篇內容介紹了“javascript Async函數相關知識點有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

前言

在異步處理方案中,目前最為簡潔優雅的便是async函數(以下簡稱A函數)。經過必要的分塊包裝后,A函數能使多個相關的異步操作如同同步操作一樣聚合起來,使其相互間的關系更為清晰、過程更為簡潔、調試更為方便。它本質是Generator函數的語法糖,通俗的說法是使用G函數進行異步處理的增強版。

嘗試

學習A函數必須有Promise基礎,***還了解Generator函數,有需要的可查看延伸小節。

為了直觀的感受A函數的魅力,下面使用Promise和A函數進行了相同的異步操作。該異步的目的是獲取用戶的留言列表,需要分頁,分頁由后臺控制。具體的操作是:先獲取到留言的總條數,再更正當前需要顯示的頁數(每次切換到不同頁時,總數目可能會發生變化),***傳遞參數并獲取到相應的數據。

let totalNum = 0; // Total comments number.  let curPage = 1; // Current page index.  let pageSize = 10; // The number of comment displayed in one page.  // 使用A函數的主代碼。  async function dealWithAsync() {  totalNum = await getListCount();  console.log('Get count', totalNum); if (pageSize * (curPage - 1) > totalNum) {  curPage = 1;  }  return getListData();  }  // 使用Promise的主代碼。  function dealWithPromise() {  return new Promise((resolve, reject) => {  getListCount().then(res => {  totalNum = res;  console.log('Get count', res);  if (pageSize * (curPage - 1) > totalNum) {  curPage = 1;  }  return getListData()  }).then(resolve).catch(reject);  });  }  // 開始執行dealWithAsync函數。  // dealWithAsync().then(res => {  // console.log('Get Data', res)  // }).catch(err => {  // console.log(err);  // });  // 開始執行dealWithPromise函數。  // dealWithPromise().then(res => {  // console.log('Get Data', res)  // }).catch(err => {  // console.log(err);  // });  function getListCount() { return createPromise(100).catch(() => {  throw 'Get list count error';  });  }  function getListData() {  return createPromise([], {  curPage: curPage,  pageSize: pageSize,  }).catch(() => {  throw 'Get list data error';  });  }  function createPromise(  data, // Reback data  params = null, // Request params  isSucceed = true,  timeout = 1000,  ) {  return new Promise((resolve, reject) => {  setTimeout(() => {  isSucceed ? resolve(data) : reject(data);  }, timeout);  });  }

對比dealWithAsync和dealWithPromise兩個簡單的函數,能直觀的發現:使用A函數,除了有await關鍵字外,與同步代碼無異。而使用Promise則需要根據規則增加很多包裹性的鏈式操作,產生了太多回調函數,不夠簡約。另外,這里分開了每個異步操作,并規定好各自成功或失敗時傳遞出來的數據,近乎實際開發。

1 登堂

1.1 形式

A函數也是函數,所以具有普通函數該有的性質。不過形式上有兩點不同:一是定義A函數時,function關鍵字前需要有async關鍵字(意為異步),表示這是個A函數。二是在A函數內部可以使用await關鍵字(意為等待),表示會將其后面跟隨的結果當成異步操作并等待其完成。

以下是它的幾種定義方式。

// 聲明式  async function A() {}  // 表達式  let A = async function () {};  // 作為對象屬性  let o = {  A: async function () {}  };  // 作為對象屬性的簡寫式  let o = {  async A() {}  };  // 箭頭函數  let o = {  A: async () => {}  };

1.2 返回值

執行A函數,會固定的返回一個Promise對象。

得到該對象后便可監設置成功或失敗時的回調函數進行監聽。如果函數執行順利并結束,返回的P對象的狀態會從等待轉變成成功,并輸出return命令的返回結果(沒有則為undefined)。如果函數執行途中失敗,JS會認為A函數已經完成執行,返回的P對象的狀態會從等待轉變成失敗,并輸出錯誤信息。

// 成功執行案例  A1().then(res => {  console.log('執行成功', res); // 10  });  async function A1() {  let n = 1 * 10;  return n;  }  // 失敗執行案例  A2().catch(err => {  console.log('執行失敗', err); // i is not defined.  });  async function A2() {  let n = 1 * i;  return n;  }

1.3 await

只有在A函數內部才可以使用await命令,存在于A函數內部的普通函數也不行。

引擎會統一將await后面的跟隨值視為一個Promise,對于不是Promise對象的值會調用Promise.resolve()進行轉化。即便此值為一個Error實例,經過轉化后,引擎依然視其為一個成功的Promise,其數據為Error的實例。

當函數執行到await命令時,會暫停執行并等待其后的Promise結束。如果該P對象最終成功,則會返回成功的返回值,相當將await  xxx替換成返回值。如果該P對象最終失敗,且錯誤沒有被捕獲,引擎會直接停止執行A函數并將其返回對象的狀態更改為失敗,輸出錯誤信息。

***,A函數中的return x表達式,相當于return await x的簡寫。

// 成功執行案例  A1().then(res => {  console.log('執行成功', res); // 約兩秒后輸出100。  });  async function A1() {  let n1 = await 10;  let n2 = await new Promise(resolve => {  setTimeout(() => {  resolve(10);  }, 2000);  });  return n1 * n2;  }  // 失敗執行案例  A2().catch(err => {  console.log('執行失敗', err); // 約兩秒后輸出10。  });  async function A2() {  let n1 = await 10;  let n2 = await new Promise((resolve, reject) => {  setTimeout(() => {  reject(10);  }, 2000);  });  return n1 * n2; }

2 入室

2.1 繼發與并發

對于存在于JS語句(for, while等)的await命令,引擎遇到時也會暫停執行。這意味著可以直接使用循環語句處理多個異步。

以下是處理繼發的兩個例子。A函數處理相繼發生的異步尤為簡潔,整體上與同步代碼無異。

// 兩個方法A1和A2的行為結果相同,都是每隔一秒輸出10,輸出三次。  async function A1() {  let n1 = await createPromise();  console.log('N1', n1);  let n2 = await createPromise();  console.log('N2', n2);  let n3 = await createPromise();  console.log('N3', n3);  }  async function A2() {  for (let i = 0; i< 3; i++) {  let n = await createPromise();  console.log('N' + (i + 1), n);  }  }  function createPromise() {  return new Promise(resolve => {  setTimeout(() => {  resolve(10);  }, 1000);  });  }

接下來是處理并發的三個例子。A1函數使用了Promise.all生成一個聚合異步,雖然簡單但靈活性降低了,只有都成功和失敗兩種情況。A3函數相對A2僅僅為了說明應該怎樣配合數組的遍歷方法使用async函數。重點在A2函數的理解上。

A2函數使用了循環語句,實際是繼發的獲取到各個異步值,但在總體的時間上相當并發(這里需要好好理解一番)。因為一開始創建reqs數組時,就已經開始執行了各個異步,之后雖然是逐一繼發獲取,但總花費時間與遍歷順序無關,恒等于耗時最多的異步所花費的時間(不考慮遍歷、執行等其它的時間消耗)。

// 三個方法A1, A2和A3的行為結果相同,都是在約一秒后輸出[10, 10, 10]。  async function A1() { let res = await Promise.all([createPromise(), createPromise(), createPromise()]);  console.log('Data', res);  }  async function A2() {  let res = [];  let reqs = [createPromise(), createPromise(), createPromise()];  for (let i = 0; i< reqs.length; i++) {  res[i] = await reqs[i];  }  console.log('Data', res);  }  async function A3() {  let res = [];  let reqs = [9, 9, 9].map(async (item) => {  let n = await createPromise(item);  return n + 1;  });  for (let i = 0; i< reqs.length; i++) {  res[i] = await reqs[i];  }  console.log('Data', res);  }  function createPromise(n = 10) {  return new Promise(resolve => {  setTimeout(() => {  resolve(n);  }, 1000);  });  }

2.2 錯誤處理

一旦await后面的Promise轉變成rejected,整個async函數便會終止。然而很多時候我們不希望因為某個異步操作的失敗,就終止整個函數,因此需要進行合理錯誤處理。注意,這里所說的錯誤不包括引擎解析或執行的錯誤,僅僅是狀態變為rejected的Promise對象。

處理的方式有兩種:一是先行包裝Promise對象,使其始終返回一個成功的Promise。二是使用try.catch捕獲錯誤。

// A1和A2都執行成,且返回值為10。  A1().then(console.log);  A2().then(console.log);  async function A1() {  let n;  n = await createPromise(true);  return n;  }  async function A2() {  let n;  try {  n = await createPromise(false);  } catch (e) {  n = e;  }  return n;  }  function createPromise(needCatch) {  let p = new Promise((resolve, reject) => {  reject(10);  });  return needCatch ? p.catch(err => err) : p;  }

2.3 實現原理

前言中已經提及,A函數是使用G函數進行異步處理的增強版。既然如此,我們就從其改進的方面入手,來看看其基于G函數的實現原理。A函數相對G函數的改進體現在這幾個方面:更好的語義,內置執行器和返回值是Promise。

更好的語義。G函數通過在function后使用*來標識此為G函數,而A函數則是在function前加上async關鍵字。在G函數中可以使用yield命令暫停執行和交出執行權,而A函數是使用await來等待異步返回結果。很明顯,async和await更為語義化。

// G函數  function* request() {  let n = yield createPromise();  }  // A函數  async function request() {  let n = await createPromise();  }  function createPromise() {  return new Promise(resolve => {  setTimeout(() => {  resolve(10);  }, 1000);  });  }

內置執行器。調用A函數便會一步步自動執行和等待異步操作,直到結束。如果需要使用G函數來自動執行異步操作,需要為其創建一個自執行器。通過自執行器來自動化G函數的執行,其行為與A函數基本相同。可以說,A函數相對G函數***改進便是內置了自執行器。

// 兩者都是每隔一秒鐘打印出10,重復兩次。  // A函數  A();  async function A() {  let n1 = await createPromise();  console.log(n1);  let n2 = await createPromise();  console.log(n2);  }  // G函數,使用自執行器執行。  spawn(G);  function* G() {  let n1 = yield createPromise();  console.log(n1); let n2 = yield createPromise();  console.log(n2);  }  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); });  });  }  function createPromise() {  return new Promise(resolve => {  setTimeout(() => { resolve(10);  }, 1000);  });  }

2.4 執行順序

在了解A函數內部與包含它外部間的執行順序前,需要明白兩點:一為Promise的實例方法是推遲到本輪事件末尾才執行的后執行操作,詳情請查看鏈接。二為Generator函數是通過調用實例方法來切換執行權進而控制程序執行順序,詳情請查看鏈接。理解好A函數的執行順序,能更加清楚的把握此三者的存在。

先看以下代碼,對比A1、A2和A3方法的結果。

F(A1); // 接連打印出:1 3 4 2 5。F(A2); // 接連打印出:1 3 2 4 5。F(A3); // 先打印出:1 3 2,隔兩秒后打印出:4 9。function F(A) {  console.log(1);  A().then(console.log);  console.log(2);  }  async function A1() {  console.log(3);  console.log(4);  return 5;  }  async function A2() {  console.log(3);  let n = await 5;  console.log(4);  return n;  }  async function A3() {  console.log(3);  let n = await createPromise();  console.log(4);  return n;  }  function createPromise() {  return new Promise(resolve => {  setTimeout(() => {  resolve(9);  }, 2000);  });  }

從結果上可歸納出一些表面形態。執行A函數,會即刻執行其函數體,直到遇到await命令。遇到await命令后,執行權會轉向A函數外部,即不管A函數內部執行而開始執行外部代碼。執行完外部代碼(本輪事件)后,才繼續執行之前await命令后面的代碼。

歸納到此已成功一半,之后著手分析其成因。如果客官您對本樓有所了解,那一定不會忘記&lsquo;自執行器&rsquo;這位大嬸吧?估計是忘記了。A函數的本質就是帶有自執行器的G函數,所以探究A函數的執行原理就是探究使用自執行器的G函數的執行原理。想起了?

再看下面代碼,使用相同邏輯的G函數會得到與A函數相同的結果。

F(A); // 先打印出:1 3 2,隔兩秒后打印出:4 9。  F(() => {  return spawn(G);  }); // 先打印出:1 3 2,隔兩秒后打印出:4 9。  function F(A) {  console.log(1);  A().then(console.log);  console.log(2);  }  async function A() {  console.log(3);  let n = await createPromise();  console.log(4);  return n;  }  function* G() {  console.log(3);  let n = yield createPromise();  console.log(4);  return n;  }  function createPromise() {  return new Promise(resolve => {  setTimeout(() => {  resolve(9);  }, 2000);  });  }  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); });  });  }

自動執行G函數時,遇到yield命令后會使用Promise.resolve包裹其后的表達式,并為其設置回調函數。無論該Promise是立刻有了結果還是過某段時間之后,其回調函數都會被推遲到在本輪事件末尾執行。之后再是下一步,再下一步。同樣的道理適用于A函數,當遇到await命令時(此處略去三五字),所以有了如此這般的執行順序。

“javascript Async函數相關知識點有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

凤庆县| 吕梁市| 溧水县| 全州县| 景泰县| 磐石市| 民县| 平顶山市| 罗定市| 玉林市| 浮山县| 稻城县| 湾仔区| 滨州市| 彰化县| 莱芜市| 西乡县| 宜兰县| 平陆县| 涟水县| 鄂伦春自治旗| 兰西县| 石棉县| 丘北县| 平利县| 安多县| 永平县| 博罗县| 舞钢市| 兴安盟| 特克斯县| 商丘市| 宁化县| 新乐市| 永福县| 保靖县| 翼城县| 确山县| 天等县| 额敏县| 五原县|