最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript中異步編程的實現(xiàn)方式詳細講解

 更新時間:2025年10月20日 11:10:37   作者:flower_tomb  
JavaScript異步編程是一種讓程序在處理耗時任務(wù)時不阻塞主線程的技術(shù),它通過回調(diào)函數(shù)、Promise和async/await等機制來實現(xiàn)任務(wù)的并發(fā)執(zhí)行,這篇文章主要介紹了JavaScript中異步編程的實現(xiàn)方式,需要的朋友可以參考下

一:回調(diào)函數(shù)

通過將函數(shù)作為參數(shù)傳遞到異步任務(wù)中,在任務(wù)完成后執(zhí)行回調(diào)。

‌優(yōu)點‌:實現(xiàn)簡單,兼容性極佳(支持所有JS環(huán)境)

‌缺點‌:嵌套過深時會導致“回調(diào)地獄”,代碼可讀性和可維護性差

示例:

function fetchData(callback) {
  setTimeout(() => callback("數(shù)據(jù)"), 1000);
}
fetchData(data => console.log(data));

二:事件監(jiān)聽

異步任務(wù)的執(zhí)行由事件觸發(fā),通過addEventListener綁定回調(diào)。

‌優(yōu)點‌:支持多事件綁定,實現(xiàn)模塊化解耦

‌支持多事件綁定‌
‌一個目標對象(如DOM元素)可同時綁定多個不同類型的事件(click/mouseover等),> ‌彼此互不干擾。
‌代碼組織更靈活,例如:
element.addEventListener(‘click’, handleClick);
element.addEventListener(‘mouseenter’, showTooltip);
‌模塊化解耦‌
‌生產(chǎn)者-消費者分離‌:事件觸發(fā)方(如按鈕)無需知道誰在處理事件,只需發(fā)出事件。
‌低耦合架構(gòu)‌:不同模塊通過事件通信,避免直接相互調(diào)用。例如:
// 模塊A:發(fā)布事件
document.dispatchEvent(new CustomEvent(‘dataLoaded’, { detail: data }));
// 模塊B:訂閱事件(無需知道模塊A的存在)
document.addEventListener(‘dataLoaded’, (e) => updateUI(e.detail));

‌缺點‌:流程控制不直觀,需依賴事件驅(qū)動架構(gòu)

document.addEventListener('click', () => console.log("事件觸發(fā)"));

三:發(fā)布訂閱模式

通過事件中心調(diào)度訂閱者和發(fā)布者,實現(xiàn)完全解耦

優(yōu)點‌:支持多對多通信,擴展性強

‌缺點‌:需手動管理訂閱關(guān)系,調(diào)試復雜度較高

【本菜鳥還不太熟悉,后續(xù)總結(jié)】

四、Promise【詳解】

Promise是異步編程的一種解決方案,Promise 是一個構(gòu)造函數(shù),接收一個函數(shù)作為參數(shù),返回一個 Promise 實例。一個 Promise 實例有三種狀態(tài),分別是pending、resolved 和 rejected,分別代表了進行中、已成功和已失敗。實例的狀態(tài)只能由 pending 轉(zhuǎn)變 resolved 或者rejected 狀態(tài),并且狀態(tài)一經(jīng)改變,就凝固了,無法再被改變了。狀態(tài)的改變是通過 resolve() 和 reject() 函數(shù)來實現(xiàn)的,可以在異步操作結(jié)束后調(diào)用這兩個函數(shù)改變 Promise 實例的狀態(tài),

1:先手寫一個簡單的Promise構(gòu)造函數(shù),從手寫的過程可以大致理解Promise的原理

    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    function MyPromise (fn) {
      var self = this;// 保存初始化狀態(tài)
      this.state = PENDING;// 初始化狀態(tài)
      this.value = null;// 用于保存 resolve 或者 rejected 傳入的值
      this.resolvedCallbacks = [];// 用于保存 resolve 的回調(diào)函數(shù)
      this.rejectedCallbacks = [];// 用于保存 reject 的回調(diào)函數(shù)
      // 狀態(tài)轉(zhuǎn)變?yōu)?resolved 方法
      function resolve (value) {
        // 判斷傳入元素是否為 Promise 值,如果是,則狀態(tài)改變必須等待前一個狀態(tài)改變后再進行改變
        if (value instanceof MyPromise) {
          return value.then(resolve, reject);
        }
        // 保證代碼的執(zhí)行順序為本輪事件循環(huán)的末尾
        setTimeout(() => {
          // 只有狀態(tài)為 pending 時才能轉(zhuǎn)變,
          if (self.state === PENDING) {
            self.state = RESOLVED;   // 修改狀態(tài)
            self.value = value;  // 設(shè)置傳入的值
            // 執(zhí)行回調(diào)函數(shù)
            self.resolvedCallbacks.forEach(callback => {
              callback(value);
            });
          }
        }, 0);
      }
      function reject (value) {
        setTimeout(() => {
          if (self.state === PENDING) {
            self.state = REJECTED;            
            self.value = value;           
            self.rejectedCallbacks.forEach(callback => {
              callback(value);
            });
          }
        }, 0);
      }
      // 將兩個方法傳入函數(shù)執(zhí)行
      try {
        fn(resolve, reject);
      } catch (e) {
        // 遇到錯誤時,捕獲錯誤,執(zhí)行 reject 函數(shù)
        reject(e);
      }
    }
    MyPromise.prototype.then = function (onResolved, onRejected) {
      // 首先判斷兩個參數(shù)是否為函數(shù)類型,因為這兩個參數(shù)是可選參數(shù)
      onResolved =
        typeof onResolved === "function"
          ? onResolved
          : function (value) {
            return value;
          };
      onRejected =
        typeof onRejected === "function"
          ? onRejected
          : function (error) {
            throw error;
          };
      // 如果是等待狀態(tài),則將函數(shù)加入對應列表中
      if (this.state === PENDING) {
        this.resolvedCallbacks.push(onResolved);
        this.rejectedCallbacks.push(onRejected);
      }
      // 如果狀態(tài)已經(jīng)凝固,則直接執(zhí)行對應狀態(tài)的函數(shù)
      if (this.state === RESOLVED) {
        onResolved(this.value);
      }
      if (this.state === REJECTED) {
        onRejected(this.value);
      }
    };

看上去有點復雜,時間有限,所以我決定放棄這個promise的手寫和原理,將各種用法弄清楚。

2:promise使用示例:

構(gòu)造函數(shù)Promise接收一個函數(shù)作為參數(shù),這個函數(shù)帶有兩個參數(shù),一個是resolve,一個是reject,這個函數(shù)內(nèi)部的代碼本身是同步的立即執(zhí)行函數(shù), 只有執(zhí)行resolve或者reject的時候是異步操作, 實際會先執(zhí)行對應的then/catch等,將then/catch里的代碼放進微任務(wù)隊列中,當主棧完成后,才會去調(diào)用resolve/reject中存放的方法執(zhí)行。

附:宏任務(wù)微任務(wù)相關(guān)js事件循環(huán)機制見另一篇博:js的事件循環(huán)機制

  // Promise構(gòu)造函數(shù)接受一個函數(shù)(執(zhí)行器函數(shù))作為參數(shù),
  // 該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù),
  // 由 JavaScript 引擎提供,不用自己部署。
  const promise = new Promise(function (resolve, reject) {
    // ... some code
 
    if (/* 異步操作成功 */) {
      // 在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)value傳遞出去;
      resolve(value);
    } else {
      // 在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)error/reason傳遞出去。
      reject(error);
    }
  });

Promise實例的then方法接收兩個回調(diào)函數(shù)作為參數(shù),第一個回調(diào)當resolve()時執(zhí)行,是成功回調(diào),第二個回調(diào)reject()時執(zhí)行,是失敗回調(diào)。

promise.then(function(value) {
    // success
}, function(error) {
    // failure
});

但同時Promise實例的catch方法也可以捕獲異常,這時候我有點疑惑,既然then方法已經(jīng)可以指出失敗回調(diào),那catch方法的意義是什么?先查了一下區(qū)別

then的失敗回調(diào)‌
僅能捕獲當前Promise鏈中‌前一個Promise‌的reject狀態(tài)或拋出的錯誤
catch方法‌
能捕獲‌整個Promise鏈‌中任意位置的未處理錯誤(包括then中拋出的異常)

這個解釋明確的說明catch在promise的鏈式調(diào)用中更好用,可以將錯誤處理集中在鏈式調(diào)用的末尾。這時候我又產(chǎn)生了兩個疑惑,第一個是鏈式調(diào)用是什么,第二個是如果集中處理了,那catch如何直到捕獲的錯誤是哪一步的錯誤
然后針對第一個問題,我開始學習promise的鏈式調(diào)用

3:promise的鏈式調(diào)用

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("成功!"), 1000);
});
 
p1.then(result => {
    console.log(result); // 輸出:成功!
    return "下一步"; // 返回一個值,可以被下一個.then()捕獲
}).then(nextStep => {
    console.log(nextStep); // 輸出:下一步
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve("最終結(jié)果"), 500);
    });
}).then(finalResult => {
    console.log(finalResult); // 輸出:最終結(jié)果
});

首先,我已知promise的then方法返回的是一個新的promise,從這段代碼可以看出,p1的then方法的成功回調(diào)中返回一個字符串—‘下一步’,直接被下一個then方法捕獲了。換種說法就是,首先,第一個then函數(shù)本身返回了一個新的promise-------------
當你then里面的回調(diào)函數(shù)返回一個非promise的值的時候,這個新的promise會立即resolve該值,也就是直接執(zhí)行第二個then方法的第一個回調(diào)。

當你then里面的回調(diào)函數(shù)返回一個promise時候,then返回新的promise會跟隨你在回調(diào)函數(shù)中返回的promise的狀態(tài)[你也可以看成同一個promise,沒有差別,反正都是執(zhí)行相同的回調(diào)]。

略繞,總之這樣鏈式調(diào)用最大好處就是整個鏈條能保持清晰的執(zhí)行順序和統(tǒng)一的錯誤處理

4:鏈式調(diào)用的catch回調(diào)

【1】.catch()會捕獲‌第一個錯誤(無論是reject的錯誤還是throw 的異常),后續(xù)錯誤會被靜默忽略。

【2】如果同時存在then的錯誤回調(diào)和獨立的catch方法,遵循就近原則和互斥執(zhí)行【只執(zhí)行最近那個】

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => reject("失敗"), 1000);
    });
    promise.then(result => {
      return "下一步"; 
    },(error)=>{
      return 'error'  // 只執(zhí)行這個
    }).then(nextStep => {
      console.log(nextStep); 
      return new Promise((resolve, reject) => {
        setTimeout(() => reject("第二步報錯"), 500);
      });
    }).then(finalResult => {
      console.log(finalResult); 
    }).catch(error=>{
      console.log(error)
    });

5:promise的其他方法

all()

使用場景:并行請求多個接口后統(tǒng)一處理數(shù)據(jù),批量文件上傳全部完成后觸發(fā)通知

// 模擬三個異步API請求
const fetchUser = () => 
  new Promise(resolve => setTimeout(() => resolve({id: 1, name: 'Alice'}), 800));

const fetchOrders = () =>
  new Promise(resolve => setTimeout(() => resolve([101, 102, 103]), 500));

const fetchProducts = () =>
  new Promise(resolve => setTimeout(() => resolve(['Laptop', 'Phone']), 300));

// 使用Promise.all并行執(zhí)行
Promise.all([fetchUser(), fetchOrders(), fetchProducts()])
  .then(([user, orders, products]) => {
    console.log('整合數(shù)據(jù):', { user, orders, products });
  })
  .catch(err => console.error('請求失敗:', err));

race()

采用競速模式,返回第一個敲定狀態(tài)(無論成功/失?。┑腜romise結(jié)果

使用場景:請求超時控制:將目標請求與setTimeout的reject Promise競速。【現(xiàn)在axios等工具直接傳入timeout參數(shù)即可】

// 模擬API請求(2秒返回)
const apiRequest = new Promise(resolve => 
  setTimeout(() => resolve("數(shù)據(jù)獲取成功"), 2000)
);

// 設(shè)置1秒超時
const timeout = new Promise((_, reject) =>
  setTimeout(() => reject("請求超時"), 1000)
);

// 競速執(zhí)行
Promise.race([apiRequest, timeout])
  .then(res => console.log(res))
  .catch(err => console.error(err));

finally()

使用場景:隱藏加載動畫無論請求成功與否

fetchData()
  .then(res => console.log(res))
  .catch(err => console.error(err.message))
  .finally(() => {
    console.log("無論成功失敗,都會執(zhí)行清理工作");
    document.getElementById('loading').style.display = 'none';
  });

6:附加一個執(zhí)行順序判斷的示例,測試下對promise的了解程度

console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 輸出順序: script start->promise1->promise1 end->script end->promise2->settimeout

五、Async/Await

async 將一個函數(shù)標記為異步函數(shù),await 需要在異步函數(shù)中使用,標記當前操作是異步操作。async + await 必須配合 promise 使用,同時 async 和 await 必須一起使用。即 await 必須在 async 標記的函數(shù)中使用。

以下是一個使用示例:

function getProfile() {
    return new Promise((resolve, reject) => {
        // 使用定時器模擬接口請求
        setTimeout(() => {
            resolve({
                code: 200,
                msg: "用戶信息",
                data: {
                    id: 1,
                    name: "liang"
                }
            })
        }, 3000);
    });
}
// 以下代碼會執(zhí)行 先輸出 123 再執(zhí)行輸出 res
function loadData() {
    getProfile().then(res => {
        console.log(res);
    })
    console.log(123);
}
// 下面寫法會使 getProfile() 先執(zhí)行
// 等待三秒后執(zhí)行完再把得到的結(jié)果賦值給左邊的res,然后再繼續(xù)往下執(zhí)行
async function loadData() {
    const res = await getProfile()
    console.log(res);
    console.log(123);
}

我的理解:async創(chuàng)建了一個異步環(huán)境,在這個環(huán)境中其實是不存在同步代碼的。

首先在使用時每個await后面理論上都需要緊跟一個promise,例如:

    async function asyncChain () {
      try {
        const stage1 = await new Promise(resolve => setTimeout(() => resolve(1), 1000));
        console.log("第一階段結(jié)果:", stage1);
      } catch (error) {
        console.error("Async鏈錯誤:", error);
      }
    }

如果跟的是一個非 Promise 值時(如字符串、數(shù)字等),引擎會通過 Promise.resolve() 將其隱式轉(zhuǎn)換為已解決的 Promise 對象,此時代碼會立即繼續(xù)執(zhí)行,也就是跟了一個立即resolve那個字符串或者數(shù)字的promise。

例如:

async function demo() {
  const result = await 42; // 等同于 await Promise.resolve(42)
  console.log(result); // 直接輸出42
}

而await后面的代碼,比如上方代碼塊中的console.log(result),實際上相當于最近的前一個await后面緊跟的Promise的then回調(diào)里的內(nèi)容。

我:也就是await會阻塞async函數(shù)中在await之后的代碼,那是不是相當于await之后的代碼其實是當前await后面的這個promise實例的then回調(diào)?
deepseek:是的,await 之后的代碼可以視為當前 await 后面的 Promise 實例的 then 回調(diào),但兩者在實現(xiàn)機制和細節(jié)上存在差異。

async function demo() {
  const result = await somePromise; // 阻塞后續(xù)代碼
  console.log(result); // 相當于 somePromise.then(res => console.log(res))
}

六、Generator/yield

這個我寫了五年前端代碼沒有用到過。當然我是菜鳥哈哈。面試遇到就放棄好了。

到此這篇關(guān)于JavaScript中異步編程的實現(xiàn)方式的文章就介紹到這了,更多相關(guān)js異步編程實現(xiàn)方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaScript前端無法獲取響應頭原因與解決方案

    JavaScript前端無法獲取響應頭原因與解決方案

    這篇文章主要為大家詳細介紹了JavaScript前端無法獲取響應頭原因與解決方案,如 Content-Disposition,下面小編就來和大家詳細介紹一下吧
    2025-08-08
  • Js利用console計算代碼運行時間的方法示例

    Js利用console計算代碼運行時間的方法示例

    最近看了一本書,發(fā)現(xiàn)了個計算代碼執(zhí)行時間的方法,感覺還挺有用的,所以這篇文章主要給大家介紹了關(guān)于Javascript利用console計算代碼運行時間的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2017-09-09
  • JavaScript中將其他類型的值轉(zhuǎn)換為字符串的方法及規(guī)則詳解

    JavaScript中將其他類型的值轉(zhuǎn)換為字符串的方法及規(guī)則詳解

    在JavaScript中,將其他類型的值轉(zhuǎn)換為字符串是一個非常常見且重要的操作,理解這些轉(zhuǎn)換規(guī)則可以幫助我們避免類型錯誤、寫出更健壯的代碼,本文將系統(tǒng)講解各種基本類型如何轉(zhuǎn)換為字符串,需要的朋友可以參考下
    2025-07-07
  • 微信小程序?qū)崿F(xiàn)登錄頁云層漂浮的動畫效果

    微信小程序?qū)崿F(xiàn)登錄頁云層漂浮的動畫效果

    微信小程序目前的火熱程度相信不用多言,最近利用空余時間用小程序?qū)崿F(xiàn)了個動態(tài)的登錄頁效果,所以下面這篇文章主要給大家介紹了利用微信小程序?qū)崿F(xiàn)登錄頁云層漂浮動畫效果的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-05-05
  • javascript實現(xiàn)電腦和手機版樣式切換

    javascript實現(xiàn)電腦和手機版樣式切換

    這篇文章主要為大家詳細介紹了javascript實現(xiàn)電腦和手機版樣式切換,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 微信小程序使用webview頁面轉(zhuǎn)pdf文件代碼示例

    微信小程序使用webview頁面轉(zhuǎn)pdf文件代碼示例

    工作需求,將webview的內(nèi)容導出到pdf輸出,下面這篇文章主要給大家介紹了關(guān)于微信小程序使用webview頁面轉(zhuǎn)pdf文件的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-09-09
  • noscript 標簽 一個被忽視的重要標簽

    noscript 標簽 一個被忽視的重要標簽

    這篇文章主要介紹了noscript 標簽 一個被忽視的重要標簽,需要的朋友可以參考下
    2023-03-03
  • 淺談Javascript中的函數(shù)、this以及原型

    淺談Javascript中的函數(shù)、this以及原型

    下面小編就為大家?guī)硪黄獪\談Javascript中的函數(shù)、this以及原型。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-10-10
  • Array, Array Constructor, for in loop, typeof, instanceOf

    Array, Array Constructor, for in loop, typeof, instanceOf

    雖然在 JavaScript 中數(shù)組是是對象,但是沒有好的理由去使用 `for in` 循環(huán) 遍歷數(shù)組。相反,有一些好的理由不去使用 for in 遍歷數(shù)組。
    2011-09-09
  • JS利用cookie記憶當前位置的防刷新導航效果

    JS利用cookie記憶當前位置的防刷新導航效果

    這篇文章主要介紹了JS利用cookie記憶當前位置的防刷新導航效果,涉及JavaScript操作cookie及導航樣式布局的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10

最新評論

搜索| 抚宁县| 新密市| 凤庆县| 孙吴县| 黄龙县| 如皋市| 浙江省| 育儿| 溧阳市| 四川省| 龙游县| 平舆县| 金昌市| 金坛市| 徐州市| 阳西县| 天柱县| 日喀则市| 邢台市| 深泽县| 太原市| 阿拉善盟| 全南县| 将乐县| 通州市| 大名县| 库尔勒市| 田东县| 城固县| 吉水县| 辽源市| 普兰县| 湾仔区| 钟祥市| 藁城市| 亚东县| 民勤县| 桃江县| 河源市| 青州市|