<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Promise 向左,Async/Await 向右?

          共 7195字,需瀏覽 15分鐘

           ·

          2022-01-16 14:44

          1. 前言

          從事前端開發(fā)至今,異步問題經(jīng)歷了 Callback Hell 的絕望,Promise/Deffered 的規(guī)范混戰(zhàn),到 Generator 的所向披靡,到如今 Async/Await 為大眾所接受,這其中 Promise 和 Async/Await 依然活躍代碼中,對(duì)他們的認(rèn)識(shí)和評(píng)價(jià)也經(jīng)歷多次反轉(zhuǎn),也有各自的擁躉,形成了一直延續(xù)至今的愛恨情仇,其背后的思考和啟發(fā),依舊值得我們深思。

          預(yù)先聲明:

          本文的目標(biāo)并不是引發(fā)大家的論戰(zhàn),也不想去推崇其中任何一種方式來作為前端異步的唯一最佳實(shí)踐,想在介紹下 Promise 和 Async/Await 知識(shí)和背后的趣事的基礎(chǔ)上,探究下這些爭(zhēng)議下隱藏的共識(shí)。

          2. Promise

          Promise 是異步編程的一種解決方案,相對(duì)于傳統(tǒng)的回調(diào)地獄更加合理和強(qiáng)大。

          所謂 Promise,簡(jiǎn)單來說就是一個(gè)容器,里面存儲(chǔ)個(gè)未來才會(huì)結(jié)束的時(shí)間的結(jié)果。從語法上說,Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理。其內(nèi)部狀態(tài)如下:

          狀態(tài)之間的流轉(zhuǎn)是不可逆的,代碼書寫如下:

          function httpPromise(): Promise<{ success: boolean; data: any }> {
          return new Promise((resolve, reject) => {
          try {
          setTimeout(() => {
          resolve({ success: true, data: {} });
          }, 1000);
          } catch (error) {
          reject(error);
          }
          });
          }
          httpPromise().then((res) => {}).catch((error) => {}).finally(() => {});

          從語法角度上看,更加容易理解,但是美中不足的就是不夠簡(jiǎn)潔,無法斷點(diǎn),冗余的匿名函數(shù)。

          2.1. Promise 是如何實(shí)現(xiàn)的?

          在剛?cè)胄械臅r(shí)候也去研究過《如何實(shí)現(xiàn)一個(gè) Promise》這個(gè)課題,嘗試寫了下如下的代碼。

          class promise {
          constructor(handler) {
          this.resolveHandler = null;
          this.rejectedHandler = null;
          setTimeout(() => {
          handler(this.resolveHandler, this.rejectedHandler);
          }, 0);
          }

          then(resolve, reject) {
          this.resolveHandler = resolve;
          this.rejectedHandler = reject;
          returnthis;
          }
          }
          function getPromise() {
          return new promise((resolve, reject) => {
          setTimeout(() => {
          resolve(20);
          }, 1000);
          });
          }
          getPromise().then((res) => {
          console.log(res);
          }, (error) => {
          console.log(error);
          });

          雖然羞恥,但是不得不說當(dāng)時(shí)還是挺滿足的,后面發(fā)現(xiàn)無法解決異步注冊(cè)的問題。

          const promise1 = getPromise();
          setTimeout(() => {
          promise1.then((data) => {
          console.log(data);
          }).catch((error) => {
          console.error(error);
          });
          }, 0);

          function getFPromise() {
          return new Promise((resolve, reject) => {
          setTimeout(() => resolve(20), 1000);
          });
          }
          // 執(zhí)行情況 報(bào)錯(cuò)
          // Uncaught TypeError: promise1.then(...).catch is not a function
          // Uncaught TypeError: resolve is not a function

          // vs 官方Promise實(shí)現(xiàn)
          const promise2 = getFPromise();
          setTimeout(() => {
          promise2.then((data) => {
          console.log(data);
          }).catch((error) => {
          console.error(error);
          });
          }, 0);
          // 執(zhí)行情況,符合預(yù)期
          // 20

          對(duì)于這一部分有興趣的同學(xué)可以自行查找標(biāo)準(zhǔn)版的實(shí)現(xiàn)方案,不過在這個(gè)探索過程中確實(shí)勾起對(duì)基礎(chǔ)知識(shí)的興趣,這也是本文去挖掘這部分知識(shí)的初衷。

          接下來看看 Async/Await 吧。

          3. Async/Await

          Async/Await 并不是什么新鮮的概念,事實(shí)的確如此。

          早在 2012 年微軟的 C# 語言發(fā)布 5.0 版本時(shí),就正式推出了 Async/Await 的概念,隨后在 Python 和 Scala 中也相繼出現(xiàn)了 Async/Await 的身影。再之后,才是我們今天討論的主角,ES 2016 中正式提出了 Async/Await 規(guī)范。

          以下是一個(gè)在 C# 中使用 Async/Await 的示例代碼:

          public async Task<int> SumPageSizesAsync(IList uris)
          {
          int total = 0;
          foreach (var uri in uris) {
          statusText.Text = string.Format("Found {0} bytes ...", total);
          var data = await new WebClient().DownloadDataTaskAsync(uri);
          total += data.Length;
          }
          statusText.Text = string.Format("Found {0} bytes total", total);
          return total;
          }

          再看看在 JavaScript 中的使用方法:

          async function httpRequest(value) {
          const res = await axios.post({ ...value });
          return res;
          }

          好的設(shè)計(jì)總是會(huì)想借鑒的,不寒磣。

          其實(shí)在前端領(lǐng)域,也有不少類 Async/Await 的實(shí)現(xiàn),其中不得不提到的就是知名網(wǎng)紅之一的老趙寫的?wind.js,站在今天的角度看,windjs 的設(shè)計(jì)和實(shí)現(xiàn)不可謂不超前。

          3.1. Async/Await 是如何實(shí)現(xiàn)的?

          ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。

          這里引用阮一峰老師的描述:

          async 函數(shù)是什么?一句話,它就是 Generator 函數(shù)的語法糖。

          前文有一個(gè) Generator 函數(shù),依次讀取兩個(gè)文件。

          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);
          });
          });
          };

          const gen = function* () {
          const f1 = yield readFile('/etc/fstab');
          const f2 = yield readFile('/etc/shells');
          console.log(f1.toString());
          console.log(f2.toString());
          };

          上面代碼的函數(shù)?gen?可以寫成?async?函數(shù),就是下面這樣。

          const asyncReadFile = async function () {
          const f1 = await readFile('/etc/fstab');
          const f2 = await readFile('/etc/shells');
          console.log(f1.toString());
          console.log(f2.toString());
          };

          一比較就會(huì)發(fā)現(xiàn),async?函數(shù)就是將 Generator 函數(shù)的星號(hào)(*)替換成?async,將?yield?替換成?await,僅此而已。

          相對(duì)于 Generator 的改進(jìn)主要集中集中在:

          • 內(nèi)置執(zhí)行器

          • 更好的語義化

          • Promise 的返回值

          到這里大家會(huì)發(fā)現(xiàn),Async/Await 本質(zhì)也是 Promise 的語法糖:Async 函數(shù)返回了 Promise 對(duì)象。

          來看下實(shí)際 Babel 轉(zhuǎn)化的代碼,方便大家理解下

          async function test() {
          const img = await fetch('tiger.jpg');
          }

          // babel 轉(zhuǎn)換后
          'use strict';

          var test = function() {
          var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
          var img;
          return regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
          switch (_context.prev = _context.next) {
          case0:
          _context.next = 2;
          return fetch('tiger.jpg');

          case2:
          img = _context.sent;

          case3:
          case'end':
          return _context.stop();
          }
          }
          }, _callee, this);
          }));

          return function test() {
          return _ref.apply(this, arguments);
          };
          }();

          function _asyncToGenerator(fn) {
          return function() {
          var gen = fn.apply(this, arguments);
          return new Promise(function(resolve, reject) {
          function step(key, arg) {
          try {
          var info = gen[key](arg);
          var value = info.value;
          } catch (error) {
          reject(error);
          return;
          }
          if (info.done) {
          resolve(value);
          } else {
          return Promise.resolve(value).then(function(value) {
          step("next", value);
          }, function(err) {
          step("throw", err);
          });
          }
          }
          return step("next");
          });
          };
          }

          不難看出最終還是轉(zhuǎn)換成基于 Promise 的調(diào)用,但是本來的三行代碼被轉(zhuǎn)換成 52 行代碼,在一些場(chǎng)景下就帶來了成本。

          例如 Vue3 并沒有采用?.(可選鏈?zhǔn)讲僮鞣?hào))的原因:

          雖然使用?很簡(jiǎn)潔,但是實(shí)際打包下反而更加冗余了,增加了包的體積,影響 Vue3 的加載速度,這也是 Async/Await 這類簡(jiǎn)潔語法的痛點(diǎn)。

          暫時(shí)不考慮深層次的運(yùn)行性能,僅僅考慮代碼使用方式來看,Async/Await 是否完美無缺?

          以 “請(qǐng)求 N 次重試” 的實(shí)現(xiàn)為例:

          /**
          * @description: 限定次數(shù)來進(jìn)行請(qǐng)求
          * @example: 例如在5次內(nèi)獲取到結(jié)果
          * @description: 核心要點(diǎn)是完成tyscript的類型推定,其次高階函數(shù)
          * @param T 指定返回?cái)?shù)據(jù)類型,M指定參數(shù)類型
          */


          export default function getLimitTimeRequest<T>(task: any, times: number) {
          // 獲取axios的請(qǐng)求實(shí)例
          let timeCount = 0;
          async function execTask(resolve, reject, ...params: any[]): Promise<void> {
          if (timeCount > times) {
          reject(newError('重試請(qǐng)求失敗'));
          }
          try {
          const data: T = await task(...params);
          if (data) {
          resolve(data);
          } else {
          timeCount++;
          execTask(resolve, reject, params);
          }
          } catch (error) {
          timeCount++;
          execTask(resolve, reject, params);
          }
          }
          return function <M>(...params: M[]): Promise<T> {
          return new Promise((resolve, reject) => {
          execTask(resolve, reject, ...params);
          });
          };
          }

          常見的實(shí)現(xiàn)思路是將 Promise 的 Resolve、Reject 的句柄傳遞到迭代函數(shù)中,來控制 Promise 的內(nèi)部狀態(tài)轉(zhuǎn)化,那如果用 Async/Await 如何做?很明顯并不好做,暴露了它的一些不足:

          • 缺少復(fù)雜的控制流程,如 always、progress、pause、resume 等

          • 內(nèi)部狀態(tài)無法控制,錯(cuò)誤捕獲嚴(yán)重依賴 try/catch

          • 缺少中斷的方法,無法 abort

          當(dāng)然,站在 EMCA 規(guī)范的角度來看,有些需求可能比較少見,但是如果納入規(guī)范中,也可以減少前端程序員在挑選異步流程控制庫時(shí)的糾結(jié)了。

          4. 總結(jié)

          針對(duì)前端異步的處理方案,Promise 和 Async/Await 都是優(yōu)秀的處理方案,但是美中不足的是有一定的不足,隨著前端工程化的深入,一定會(huì)有更好的方案來迎合解決這些問題,大家不要失望,未來還是可期的。

          從 Promise 和 Async/Await 的演進(jìn)和糾結(jié)中,大家實(shí)際能夠感到前端人對(duì) JavaScript 世界的辛苦耕作和奇思妙想,這種思維和方式也可以沉淀到我們?nèi)粘5男枨箝_發(fā)中去,善于求索,辯證的去使用它們,追求更加極致的方案。



          緊追技術(shù)前沿,深挖專業(yè)領(lǐng)域
          掃碼關(guān)注我們吧!
          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天操天天操天天操天天操 | 国产91 丝袜美女在线 | 97久久人人超碰 | 黄片亚洲视频 | 高清无码在线观看免费 |