亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

Generator怎么在JavaScript中使用

發布時間:2021-03-29 17:21:38 來源:億速云 閱讀:147 作者:Leah 欄目:web開發

本篇文章給大家分享的是有關Generator怎么在JavaScript中使用,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

批處理 (或計劃)

執行 Generator 函數會返回一個遍歷器對象,那意味著通過它我們可以同步地遍歷。為什么我們想這么做?原因有可能是為了實現批處理。想象一下,我們需要下載 1000 個項目,并在表格中逐行的顯示它們(不要問我為什么,假設我們不使用框架)。雖然立刻展示它們沒有什么不好的,但有時這可能不是最好的解決方案 —— 也許你的 MacBook Pro 可以輕松處理它,但普通人的電腦不能(更別說手機了)。所以,這意味著我們需要用某種方式延遲執行。

請注意,這個例子是關于性能優化,在你遇到這個問題之前,沒必要這樣做 ——過早優化是萬惡之源!

// 最初的同步實現版本
function renderItems(items) {
 for (item of items) {
 renderItem(item);
 }
}
// 函數將由我們的執行器遍歷執行
// 實際上,我們可以用相同的同步方式來執行它!
function* renderItems(items) {
 // 我使用 for..of 遍歷方法來避免新函數的產生
 for (item of items) {
 yield renderItem(item);
 }
}

沒有什么區別是吧?那么,這里的區別在于,現在我們可以在不改變源代碼的情況下以不同方式運行這個函數。實際上,正如我之前提到的,沒有必要等待,我們可以同步執行它。所以,來調整下我們的代碼。在每個 yield 后邊加一個 4 ms(JavaScript VM 中的一個心跳) 的延遲怎么樣?我們有 1000 個項目,渲染將需要 4 秒 —— 還不錯,假設我想在 2 秒之內渲染完畢,很容易想到的方法是每次渲染 2 個。突然使用 Promise 的解決方案將變得更加復雜 —— 我們必須要傳遞另一個參數:每次渲染的項目個數。通過我們的執行器,我們仍然需要傳遞這個參數,但好處是對我們的 renderItems 方法完全沒有影響。

function runWithBatch(chunk, fn, ...args) {
 const gen = fn(...args);
 let num = 0;
 return new Promise((resolve, promiseReject) => {
 callNextStep();
 function callNextStep(res) {
  let result;
  try {
  result = gen.next(res);
  } catch (e) {
  return reject(e);
  }
  next(result);
 }
 function next({ done, value }) {
  if (done) {
  return resolve(value);
  }
  // every chunk we sleep for a tick
  if (num++ % chunk === 0) {
  return sleep(4).then(proceed);
  } else {
  return proceed();
  }
  function proceed() {
  return callNextStep(value);
  }
 }
 });
}
// 第一個參數 —— 每批處理多少個項目
const items = [...];
batchRunner(2, function*() {
 for (item of items) {
 yield renderItem(item);
 }
});

正如你所看到的,我們可以輕松改變每批處理項目的個數,不去考慮執行器,回到正常的同步執行方式 —— 所有這些都不會影響我們的 renderItems 方法。

取消

我們來考慮下傳統的功能 —— 取消。在我promises cancellation in general ( 譯文:如何取消你的 Promise? ) 這篇文章中已經詳細談到了。所以我會使用其中一些代碼:

function runWithCancel(fn, ...args) {
 const gen = fn(...args);
 let cancelled, cancel;
 const promise = new Promise((resolve, promiseReject) => {
 // define cancel function to return it from our fn
 // 定義 cancel 方法,并返回它
 cancel = () => {
  cancelled = true;
  reject({ reason: 'cancelled' });
 };
 onFulfilled();
 function onFulfilled(res) {
  if (!cancelled) {
  let result;
  try {
   result = gen.next(res);
  } catch (e) {
   return reject(e);
  }
  next(result);
  return null;
  }
 }
 function onRejected(err) {
  var result;
  try {
  result = gen.throw(err);
  } catch (e) {
  return reject(e);
  }
  next(result);
 }
 function next({ done, value }) {
  if (done) {
  return resolve(value);
  }
  // 假設我們總是接收 Promise,所以不需要檢查類型
  return value.then(onFulfilled, onRejected);
 }
 });
 return { promise, cancel };
}

這里最好的部分是我們可以取消所有還沒來得及執行的請求(也可以給我們的執行器傳遞類似AbortController 的對象參數,所以它甚至可以取消當前的請求!),而且我們沒有修改過自己業務邏輯中的一行的代碼。

暫停/恢復

另一個特殊的需求可能是暫停/恢復功能。你為什么想要這個功能?想象一下,我們渲染了 1000 行數據,而且速度非常慢,我們希望給用戶提供暫停/恢復渲染的功能,這樣他們就可以停止所有的后臺工作讀取已經下載的內容了。讓我們開始吧!

// 實現渲染的方法還是一樣的
function* renderItems() {
 for (item of items) {
 yield renderItem(item);
 }
}
function runWithPause(genFn, ...args) {
 let pausePromiseResolve = null;
 let pausePromise;
 const gen = genFn(...args);
 const promise = new Promise((resolve, reject) => {
 onFulfilledWithPromise();
 function onFulfilledWithPromise(res) {
  if (pausePromise) {
  pausePromise.then(() => onFulfilled(res));
  } else {
  onFulfilled(res);
  }
 }
 function onFulfilled(res) {
  let result;
  try {
  result = gen.next(res);
  } catch (e) {
  return reject(e);
  }
  next(result);
  return null;
 }
 function onRejected(err) {
  var result;
  try {
  result = gen.throw(err);
  } catch (e) {
  return reject(e);
  }
  next(result);
 }
 function next({ done, value }) {
  if (done) {
  return resolve(value);
  }
  // 假設我們總是接收 Promise,所以不需要檢查類型
  return value.then(onFulfilledWithPromise, onRejected);
 }
 });
 return {
 pause: () => {
  pausePromise = new Promise(resolve => {
  pausePromiseResolve = resolve;
  });
 },
 resume: () => {
  pausePromiseResolve();
  pausePromise = null;
 },
 promise
 };
}

調用這個執行器,可以給我們返回一個具有暫停/恢復功能的對象,所有這些都可以輕松得到,還是使用我們之前的業務代碼!所以,如果你有很多"沉重"的請求鏈,需要耗費很長時間,而你想給你的用戶提供暫停/恢復功能的話,你可以隨意在你的代碼中實現這個執行器。

錯誤處理

我們有個神秘的 onRejected 調用,這是我們這部分談論的主題。如果我們使用正常的 async/await 或 Promise 鏈式寫法,我們將通過 try/catch 語句來進行錯誤處理,如果不添加大量的邏輯代碼就很難進行錯誤處理。通常情況下,如果我們需要以某種方式處理錯誤(比如重試),我們只是在 Promise 內部進行處理,這將會回調自己,可能再次回到同樣的點。而且,這還不是一個通用的解決方案 —— 可悲的是,在這里甚至 Generator 也不能幫助我們。我們發現了 Generator 的局限 —— 雖然我們可以控制執行流程,但不能移動 Generator 函數的主體;所以我們不能后退一步,重新執行我們的命令。一個可行的解決方案是使用command pattern, 它告訴了我們 yield 結果的數據結構 —— 應該是我們需要執行此命令需要的所有信息,這樣我們就可以再次執行它了。所以,我們的方法需要改為:

function* renderItems() {
 for (item of items) {
 // 我們需要將所有東西傳遞出去:
 // 方法, 內容, 參數
 yield [renderItem, null, item];
 }
}

正如你所看到的,這使得我們不清楚發生了什么 —— 所以,也許最好是寫一些 wrapWithRetry 方法,它會檢查 catch 代碼塊中的錯誤類型并再次嘗試。但是我們仍然可以做一些不影響我們功能的事情。例如,我們可以增加一個關于忽略錯誤的策略 —— 在 async/await 中我們不得不使用 try/catch 包裝每個調用,或者添加空的 .catch(() => {}) 部分。有了 Generator,我們可以寫一個執行器,忽略所有的錯誤。

function runWithIgnore(fn, ...args) {
 const gen = fn(...args);
 return new Promise((resolve, promiseReject) => {
 onFulfilled();
 function onFulfilled(res) {
  proceed({ data: res });
 }
 // 這些是 yield 返回的錯誤
 // 我們想忽略它們
 // 所以我們像往常一樣做,但不去傳遞出錯誤
 function onRejected(error) {
  proceed({ error });
 }
 function proceed(data) {
  let result;
  try {
  result = gen.next(data);
  } catch (e) {
  // 這些錯誤是同步錯誤(比如 TypeError 等)
  return reject(e);
  }
  // 為了區分錯誤和正常的結果
  // 我們用它來執行
  next(result);
 }
 function next({ done, value }) {
  if (done) {
  return resolve(value);
  }
  // 假設我們總是接收 Promise,所以不需要檢查類型
  return value.then(onFulfilled, onRejected);
 }
 });
}

關于 async/await

Async/await 是現在的首選語法(甚至 co 也談到了它 ),這也是未來。但是,Generator 也在 ECMAScript 標準內,這意味著為了使用它們,除了寫幾個工具函數,你不需要任何東西。我試圖向你們展示一些不那么簡單的例子,這些實例的價值取決于你的看法。請記住,沒有那么多人熟悉 Generator,并且如果在整個代碼庫中只有一個地方使用它們,那么使用 Promise 可能會更容易一些 —— 但是另一方面,通過 Generator 某些問題可以被優雅和簡潔的處理。

以上就是Generator怎么在JavaScript中使用,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

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

AI

达日县| 石棉县| 南京市| 大港区| 曲阳县| 沐川县| 恩平市| 九龙坡区| 兴安县| 松溪县| 沈阳市| 泽州县| 宁明县| 沂南县| 咸阳市| 夏津县| 玉门市| 墨脱县| 旌德县| 麦盖提县| 颍上县| 博白县| 宜州市| 绍兴县| 来凤县| 新龙县| 巫山县| 浙江省| 调兵山市| 原阳县| 盖州市| 朝阳县| 南康市| 博客| 额尔古纳市| 万荣县| 麻栗坡县| 岚皋县| 鄂托克旗| 昌图县| 洞口县|