亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

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

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

示例:

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

二:事件監(jiān)聽

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

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

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

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

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

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

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

優(yōu)點(diǎn)‌:支持多對多通信,擴(kuò)展性強(qiáng)

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

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

四、Promise【詳解】

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

1:先手寫一個(gè)簡單的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)改變必須等待前一個(gè)狀態(tài)改變后再進(jìn)行改變
        if (value instanceof MyPromise) {
          return value.then(resolve, reject);
        }
        // 保證代碼的執(zhí)行順序?yàn)楸据喪录h(huán)的末尾
        setTimeout(() => {
          // 只有狀態(tài)為 pending 時(shí)才能轉(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);
      }
      // 將兩個(gè)方法傳入函數(shù)執(zhí)行
      try {
        fn(resolve, reject);
      } catch (e) {
        // 遇到錯(cuò)誤時(shí),捕獲錯(cuò)誤,執(zhí)行 reject 函數(shù)
        reject(e);
      }
    }
    MyPromise.prototype.then = function (onResolved, onRejected) {
      // 首先判斷兩個(gè)參數(shù)是否為函數(shù)類型,因?yàn)檫@兩個(gè)參數(shù)是可選參數(shù)
      onResolved =
        typeof onResolved === "function"
          ? onResolved
          : function (value) {
            return value;
          };
      onRejected =
        typeof onRejected === "function"
          ? onRejected
          : function (error) {
            throw error;
          };
      // 如果是等待狀態(tài),則將函數(shù)加入對應(yīng)列表中
      if (this.state === PENDING) {
        this.resolvedCallbacks.push(onResolved);
        this.rejectedCallbacks.push(onRejected);
      }
      // 如果狀態(tài)已經(jīng)凝固,則直接執(zhí)行對應(yīng)狀態(tài)的函數(shù)
      if (this.state === RESOLVED) {
        onResolved(this.value);
      }
      if (this.state === REJECTED) {
        onRejected(this.value);
      }
    };

看上去有點(diǎn)復(fù)雜,時(shí)間有限,所以我決定放棄這個(gè)promise的手寫和原理,將各種用法弄清楚。

2:promise使用示例:

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

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

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

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

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

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

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

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

3:promise的鏈?zhǔn)秸{(diào)用

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

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

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

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

4:鏈?zhǔn)秸{(diào)用的catch回調(diào)

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

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

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

5:promise的其他方法

all()

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

// 模擬三個(gè)異步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()

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

使用場景:請求超時(shí)控制:將目標(biāo)請求與setTimeout的reject Promise競速?!粳F(xiàn)在axios等工具直接傳入timeout參數(shù)即可】

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

// 設(shè)置1秒超時(shí)
const timeout = new Promise((_, reject) =>
  setTimeout(() => reject("請求超時(shí)"), 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:附加一個(gè)執(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 將一個(gè)函數(shù)標(biāo)記為異步函數(shù),await 需要在異步函數(shù)中使用,標(biāo)記當(dāng)前操作是異步操作。async + await 必須配合 promise 使用,同時(shí) async 和 await 必須一起使用。即 await 必須在 async 標(biāo)記的函數(shù)中使用。

以下是一個(gè)使用示例:

function getProfile() {
    return new Promise((resolve, reject) => {
        // 使用定時(shí)器模擬接口請求
        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)建了一個(gè)異步環(huán)境,在這個(gè)環(huán)境中其實(shí)是不存在同步代碼的。

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

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

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

例如:

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

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

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

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

六、Generator/yield

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

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

相關(guān)文章

  • 詳解小程序循環(huán)require之坑

    詳解小程序循環(huán)require之坑

    這篇文章主要介紹了詳解小程序循環(huán)require之坑,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • JavaScript canvas繪制圓弧與圓形

    JavaScript canvas繪制圓弧與圓形

    這篇文章主要為大家詳細(xì)介紹了JavaScript canvas繪制圓弧與圓形,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • js 創(chuàng)建快捷方式的代碼(fso)

    js 創(chuàng)建快捷方式的代碼(fso)

    js 創(chuàng)建快捷方式的代碼,這個(gè)是在本地運(yùn)行的需要確認(rèn)的,需要的朋友可以參考下。
    2010-11-11
  • 詳解Next.js頁面渲染的優(yōu)化方案

    詳解Next.js頁面渲染的優(yōu)化方案

    這篇文章主要介紹了詳解Next.js頁面渲染的優(yōu)化方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • hash和history路由模式區(qū)別示例解析

    hash和history路由模式區(qū)別示例解析

    這篇文章主要為大家介紹了hash和history路由模式區(qū)別示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • JavaScript實(shí)現(xiàn)將xml轉(zhuǎn)換成html table表格的方法

    JavaScript實(shí)現(xiàn)將xml轉(zhuǎn)換成html table表格的方法

    這篇文章主要介紹了JavaScript實(shí)現(xiàn)將xml轉(zhuǎn)換成html table表格的方法,實(shí)例分析了javascript操作XML文件與table表格的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • uniapp中的picker選擇器的幾種使用場景

    uniapp中的picker選擇器的幾種使用場景

    本文主要介紹了uniapp中的picker選擇器的幾種使用場景,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • webpack中splitChunks分包策略的實(shí)現(xiàn)

    webpack中splitChunks分包策略的實(shí)現(xiàn)

    splitChunks是 webpack 中用于分包的配置選項(xiàng)之一,本文主要介紹了webpack中splitChunks分包策略的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • 談?wù)凧avaScript數(shù)組常用方法總結(jié)

    談?wù)凧avaScript數(shù)組常用方法總結(jié)

    本篇文章主要介紹了談?wù)凧avaScript數(shù)組常用方法總結(jié),在JavaScript中,我們需要時(shí)常對數(shù)組進(jìn)行操作。一起跟隨小編過來看看吧
    2017-01-01
  • JavaScript知識點(diǎn)整理

    JavaScript知識點(diǎn)整理

    本文是腳本之家小編日常整理的關(guān)于javascript知識點(diǎn),包括javascript擁有的特點(diǎn),組成部分,數(shù)據(jù)類型等方面,對javascript知識點(diǎn)相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12

最新評論