日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

八個Promise高級技巧,讓你在前端開發中如虎添翼!

來源: 責編: 時間:2024-07-02 17:38:01 153觀看
導讀在JavaScript項目中,Promise 的使用是必不可少的。然而,我發現許多中高級前端開發人員仍然停留在常見的promiseInst.then()、promiseInst.catch()、Promise.all甚至async/await等常規實踐上,并沒有深入理解它們。實際上,P

在JavaScript項目中,Promise 的使用是必不可少的。然而,我發現許多中高級前端開發人員仍然停留在常見的promiseInst.then()、promiseInst.catch()、Promise.all甚至async/await等常規實踐上,并沒有深入理解它們。L7N28資訊網——每日最新資訊28at.com

實際上,Promise 有許多巧妙的高級用法,其中一些在ALOVA請求策略庫中廣泛使用。L7N28資訊網——每日最新資訊28at.com

ALOVA 是一個基于 Promise 的請求策略庫,旨在幫助開發者更高效地進行 HTTP 請求處理。它通過高級的 Promise 技巧,實現了請求共享、緩存、批量請求等功能,從而簡化了前端開發中的數據請求管理。ALOVA 的目標是提供一種簡潔、高效的方式來處理復雜的請求場景,減少代碼冗余,提高應用性能。L7N28資訊網——每日最新資訊28at.com

現在,我將毫無保留地分享這些高級技巧。讀完這篇文章后,將不再為相關問題感到困惑。L7N28資訊網——每日最新資訊28at.com

串行執行Promise數組

例如,如果你有一組需要串行執行的接口,首先你可能會想到使用 await。L7N28資訊網——每日最新資訊28at.com

const requestAry = [() => api.request1(), () => api.request2(), () => api.request3()];for (const requestItem of requestAry) {  await requestItem();}

如果使用Promise語法,你可以使用then函數將多個Promise串起來,從而實現順序執行。L7N28資訊網——每日最新資訊28at.com

const requestAry = [() => api.request1(), () => api.request2(), () => api.request3()];const finallyPromise = requestAry.reduce(    (currentPromise, nextRequest) => currentPromise.then(() => nextRequest()),    Promise.resolve() // 創建一個初始Promise以串聯數組中的Promise。);

2.在新Promise作用域外改變狀態

假設你有一些頁面上的函數需要在使用前收集用戶信息,那么你會如何實現呢?一種方法是在點擊某個功能前彈出信息收集對話框。L7N28資訊網——每日最新資訊28at.com

不同層次的前端開發人員有不同的實現思路:L7N28資訊網——每日最新資訊28at.com

  • 初級前端:我會創建一個模態框,然后復制粘貼到其他頁面以提高效率!
  • 中級前端:你的方法不利于維護。我們應該將這個組件單獨封裝,并在需要的頁面中導入使用!
  • 高級前端:封裝該封裝的!寫在一個所有頁面都能調用的地方,不是更好嗎?

讓我們看看高級前端是如何實現的。以Vue3為例,看下面的示例。L7N28資訊網——每日最新資訊28at.com

<!-- App.vue --><template>  <!-- 以下是模態組件。 -->  <div class="modal" v-show="visible">    <div>      用戶名:<input v-model="info.name" />    </div>    <!-- 其他信息 -->    <button @click="handleCancel">取消</button>    <button @click="handleConfirm">提交</button>  </div>  <!-- 頁面組件 --></template><script setup>import { provide } from 'vue';const visible = ref(false);const info = reactive({  name: ''});let resolveFn, rejectFn;// 將信息收集函數傳遞給以下內容。provide('getInfoByModal', () => {  visible.value = true;  return new Promise((resolve, reject) => {    // 將兩個函數賦值給外部,突破Promise作用域。    resolveFn = resolve;    rejectFn = reject;  });});const handleConfirm = () => {  resolveFn && resolveFn(info);};const handleCancel = () => {  rejectFn && rejectFn(new Error('用戶已取消。'));};</script>

接下來,直接調用 getInfoByModal 即可使用模態框,并輕松獲取用戶填寫的數據。L7N28資訊網——每日最新資訊28at.com

<template>  <button @click="handleClick">填寫信息</button></template><script setup>import { inject } from 'vue';const getInfoByModal = inject('getInfoByModal');const handleClick = async () => {  // 調用后,會顯示模態框。當用戶點擊“確認”時,Promise會變為fulfilled狀態,從而獲取用戶信息。  const info = await getInfoByModal();  await api.submitInfo(info);}</script>

這也是許多UI組件庫中封裝常用組件的方法。L7N28資訊網——每日最新資訊28at.com

3.async/await的替代用法

許多人只知道在調用 async function 時使用 await 接收返回值,但他們不知道async函數實際上是返回Promise的函數。例如,以下兩個函數是等價的:L7N28資訊網——每日最新資訊28at.com

const fn1 = async () => 1;const fn2 = () => Promise.resolve(1);fn1(); // 同樣返回一個值為1的Promise對象。

在大多數情況下,await 后面跟著的是一個Promise對象,并等待其變為 fulfilled 狀態。因此,下面的函數 fn1 等待也是等價的:L7N28資訊網——每日最新資訊28at.com

await fn1();const promiseInst = fn1();await promiseInst;

然而,await 有一個不為人知的秘密。當它后面跟的是非Promise對象的值時,它會使用 Promise 對象包裝該值。因此,await 后的代碼總是異步執行的。例如:L7N28資訊網——每日最新資訊28at.com

Promise.resolve().then(() => {  console.log(1);});await 2;console.log(2);// 輸出順序:1 2

等價于L7N28資訊網——每日最新資訊28at.com

Promise.resolve().then(() => {  console.log(1);});Promise.resolve().then(() => {  console.log(2);});

4.使用Promise實現請求共享

當一個請求已經發送但尚未響應時,再次發出相同請求會導致浪費請求。此時,我們可以與第二個請求共享第一個請求的響應。L7N28資訊網——每日最新資訊28at.com

request('GET', '/test-api').then(response1 => {  // ...});request('GET', '/test-api').then(response2 => {  // ...});

以上兩個請求只會發送一次,并同時接收相同的響應值。L7N28資訊網——每日最新資訊28at.com

那么,請求共享有哪些場景呢?我認為有三種:L7N28資訊網——每日最新資訊28at.com

  • 當一個頁面同時渲染多個內部組件獲取數據時;
  • 提交按鈕未禁用,用戶連續多次點擊提交按鈕;
  • 在預加載數據的情況下,進入預加載頁面前完成預加載;

這也是Alova的高級功能之一。實現請求共享需要使用 Promise 緩存功能。也就是說,一個Promise對象可以通過多次await調用獲取數據。簡單的實現思路如下:L7N28資訊網——每日最新資訊28at.com

const pendingPromises = {};function request(type, url, data) {  // 使用請求信息作為唯一請求鍵來緩存正在請求的Promise對象。  // 具有相同鍵的請求將重用該Promise。  const requestKey = JSON.stringify([type, url, data]);  if (pendingPromises[requestKey]) {    return pendingPromises[requestKey];  }  const fetchPromise = fetch(url, {    method: type,    data: JSON.stringify(data)  })  .then(response => response.json())  .finally(() => {    delete pendingPromises[requestKey];  });  return pendingPromises[requestKey] = fetchPromise;}

5.如果同時調用resolve和reject會發生什么?

大家都知道,Promise有三種狀態:pending、fulfilled 和 rejected。但在下面的例子中,Promise 的最終狀態是什么?L7N28資訊網——每日最新資訊28at.com

const promise = new Promise((resolve, reject) => {  resolve();  reject();});

正確答案是 fulfilled 狀態。我們只需記住,一旦Promise從pending狀態轉變為其他狀態,就不能再改變。因此,在這個例子中,調用 resolve()后,即使調用 reject(),狀態也不會再改變。L7N28資訊網——每日最新資訊28at.com

6.徹底弄清then/catch/finally的返回值

總結一句話,上述三個函數都會返回一個新的 Promise包裝對象,包裝的值是執行回調函數的返回值。如果回調函數拋出錯誤,它將包裝一個處于 rejected 狀態的 Promise。這不太容易理解,我們來看一個例子:L7N28資訊網——每日最新資訊28at.com

你可以將它們一個一個復制并在瀏覽器控制臺中運行,以更好地理解。L7N28資訊網——每日最新資訊28at.com

// then函數Promise.resolve().then(() => 1); // return new Promise(resolve => resolve(1))Promise.resolve().then(() => Promise.resolve(2)); // return new Promise(resolve => resolve(Promise.resolve(2)))Promise.resolve().then(() => {  throw new Error('abc')}); // return new Promise(resolve => resolve(Promise.reject(new Error('abc'))))Promise.reject().then(() => 1, () => 2); // return new Promise(resolve => resolve(2))// catch函數Promise.reject().catch(() => 3); // return new Promise(resolve => resolve(3))Promise.resolve().catch(() => 4); // 返回值是一個新的Promise,解析為調用catch的Promise對象。// 當finally函數的返回值不是Promise時,返回finally函數之前的Promise對象。Promise.resolve().finally(() => {}); // return Promise.resolve()Promise.reject().finally(() => {}); // return Promise.reject()// 當finally函數的返回值是Promise時,等待返回的Promise解析后再返回finally函數之前的Promise對象。Promise.resolve(5).finally(() => new Promise(res => {  setTimeout(res, 1000);})); // 返回一個處于pending狀態的Promise,1秒后解析為5。Promise.reject(6).finally(() => new Promise(res => {  setTimeout(res, 1000);})); // 返回一個處于pending狀態的Promise,1秒后拋出數字6。

7.then函數的第二個回調與catch回調有何不同?

Promise中的 then 函數的第二個回調和 catch 函數在請求失敗時都會被觸發。乍一看,它們似乎沒有太大區別,但實際上,前者無法捕捉當前 then 函數第一個回調函數中拋出的錯誤,而 catch 函數可以。L7N28資訊網——每日最新資訊28at.com

Promise.resolve().then(  () => {    throw new Error('成功回調中的錯誤');  },  () => {    // 不會執行  }).catch(reason => {  console.log(reason.message); // 打印“成功回調中的錯誤”});

其原理如前所述,catch 函數是在 then 函數返回的 Promise 處于 rejected 狀態時調用的,自然可以捕捉到其錯誤。L7N28資訊網——每日最新資訊28at.com

8.實現Koa2洋蔥模型中的Promise

Koa2框架引入了洋蔥模型,使你的請求像剝洋蔥一樣逐層處理,按相反順序進入和退出層,從而實現請求的統一前后處理。L7N28資訊網——每日最新資訊28at.com

圖片圖片L7N28資訊網——每日最新資訊28at.com

我們看看一個簡單的Koa2洋蔥模型:L7N28資訊網——每日最新資訊28at.com

const app = new Koa();app.use(async (ctx, next) => {  console.log('a-start');  await next();  console.log('a-end');});app.use(async (ctx, next) => {  console.log('b-start');  await next();  console.log('b-end');});app.listen(3000);

上面的輸出是 a-start -> b-start -> b-end -> a-end。這種神奇的輸出順序是如何實現的呢?我用了大約20行代碼實現了這個簡單的實現,巧合的是,它與Koa相似。L7N28資訊網——每日最新資訊28at.com

接下來,讓我們進一步分析。L7N28資訊網——每日最新資訊28at.com

保存中間件函數,然后在 listen 函數中接收到請求時調用洋蔥模型的執行。L7N28資訊網——每日最新資訊28at.com

function action(koaInstance, ctx) {  // ...}class Koa {  middlewares = [];  use(mid) {    this.middlewares.push(mid);  }  listen(port) {    // 模擬接收請求的偽代碼    http.on('request', ctx => {      action(this, ctx);    });  }}

接收到請求后,從第一個中間件開始按順序執行前置邏輯,調用 next。L7N28資訊網——每日最新資訊28at.com

// 開始中間件調用。function action(koaInstance, ctx) {  let nextMiddlewareIndex = 1; // 標識下一個執行的中間件索引    // 定義next函數。  function next() {    // 在剝洋蔥之前,調用next會調用下一個中間件函數。    const nextMiddleware = middlewares[nextMiddlewareIndex];    if (nextMiddleware) {      nextMiddlewareIndex++;      nextMiddleware(ctx, next);    }  }  // 從第一個中間件函數開始執行,并傳入ctx和next函數。  middlewares[0](ctx, next);}

處理“next”之后的后置邏輯L7N28資訊網——每日最新資訊28at.com

function action(koaInstance, ctx) {  let nextMiddlewareIndex = 1;  function next() {    const nextMiddleware = middlewares[nextMiddlewareIndex];    if (nextMiddleware) {      nextMiddlewareIndex++;      // 這里還添加了一個return,使中間件函數的執行通過Promise從后向前連接(建議反復理解這個return)。      return Promise.resolve(nextMiddleware(ctx, next));    } else {      // 在最后一個中間件的前置邏輯執行完后,返回的fulfilled Promise將開始執行next之后的后置邏輯。      return Promise.resolve();    }  }  middlewares[0](ctx, next);}

L7N28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-98199-0.html八個Promise高級技巧,讓你在前端開發中如虎添翼!

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 一文搞懂Nginx配置:輕松駕馭多域名管理的實戰攻略

下一篇: ASP.NET Core 極簡 API 在 .NET 6 中的新特性探索

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 大荔县| 中宁县| 合作市| 山丹县| 广昌县| 霍州市| 谷城县| 钟山县| 抚州市| 林甸县| 克东县| 浦东新区| 奈曼旗| 南华县| 胶州市| 渭源县| 长乐市| 宾川县| 乌拉特后旗| 宜春市| 雅江县| 鄂托克旗| 浦城县| 仁寿县| 习水县| 财经| 繁峙县| 鄂托克旗| 双江| 广平县| 皮山县| 台东市| 曲水县| 独山县| 南阳市| 辽宁省| 谷城县| 寻甸| 叙永县| 哈巴河县| 青岛市|