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

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

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

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

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

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

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

示例:

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

二:事件監(jiān)聽

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

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

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

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

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

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

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

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

‌缺點(diǎn)‌:需手動(dòng)管理訂閱關(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)改變,就凝固了,無(wú)法再被改變了。狀態(tài)的改變是通過(guò) resolve() 和 reject() 函數(shù)來(lái)實(shí)現(xiàn)的,可以在異步操作結(jié)束后調(diào)用這兩個(gè)函數(shù)改變 Promise 實(shí)例的狀態(tài),

1:先手寫一個(gè)簡(jiǎn)單的Promise構(gòu)造函數(shù),從手寫的過(guò)程可以大致理解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ù)加入對(duì)應(yīng)列表中
      if (this.state === PENDING) {
        this.resolvedCallbacks.push(onResolved);
        this.rejectedCallbacks.push(onRejected);
      }
      // 如果狀態(tài)已經(jīng)凝固,則直接執(zhí)行對(duì)應(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í)際會(huì)先執(zhí)行對(duì)應(yīng)的then/catch等,將then/catch里的代碼放進(jìn)微任務(wù)隊(duì)列中,當(dāng)主棧完成后,才會(huì)去調(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è)解釋明確的說(shuō)明catch在promise的鏈?zhǔn)秸{(diào)用中更好用,可以將錯(cuò)誤處理集中在鏈?zhǔn)秸{(diào)用的末尾。這時(shí)候我又產(chǎn)生了兩個(gè)疑惑,第一個(gè)是鏈?zhǔn)秸{(diào)用是什么,第二個(gè)是如果集中處理了,那catch如何直到捕獲的錯(cuò)誤是哪一步的錯(cuò)誤
然后針對(duì)第一個(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方法捕獲了。換種說(shuō)法就是,首先,第一個(gè)then函數(shù)本身返回了一個(gè)新的promise-------------
當(dāng)你then里面的回調(diào)函數(shù)返回一個(gè)非promise的值的時(shí)候,這個(gè)新的promise會(huì)立即resolve該值,也就是直接執(zhí)行第二個(gè)then方法的第一個(gè)回調(diào)。

當(dāng)你then里面的回調(diào)函數(shù)返回一個(gè)promise時(shí)候,then返回新的promise會(huì)跟隨你在回調(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()會(huì)捕獲‌第一個(gè)錯(cuò)誤(無(wú)論是reject的錯(cuò)誤還是throw 的異常),后續(xù)錯(cuò)誤會(huì)被靜默忽略。

【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()

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

// 模擬三個(gè)異步API請(qǐng)求
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('請(qǐng)求失敗:', err));

race()

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

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

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

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

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

finally()

使用場(chǎng)景:隱藏加載動(dòng)畫無(wú)論請(qǐng)求成功與否

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

6:附加一個(gè)執(zhí)行順序判斷的示例,測(cè)試下對(duì)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í)器模擬接口請(qǐng)求
        setTimeout(() => {
            resolve({
                code: 200,
                msg: "用戶信息",
                data: {
                    id: 1,
                    name: "liang"
                }
            })
        }, 3000);
    });
}
// 以下代碼會(huì)執(zhí)行 先輸出 123 再執(zhí)行輸出 res
function loadData() {
    getProfile().then(res => {
        console.log(res);
    })
    console.log(123);
}
// 下面寫法會(huì)使 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ù)字等),引擎會(huì)通過(guò) Promise.resolve() 將其隱式轉(zhuǎn)換為已解決的 Promise 對(duì)象,此時(shí)代碼會(huì)立即繼續(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會(huì)阻塞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è)我寫了五年前端代碼沒有用到過(guò)。當(dāng)然我是菜鳥哈哈。面試遇到就放棄好了。

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

相關(guān)文章

最新評(píng)論

前郭尔| 江油市| 平南县| 武乡县| 台前县| 博兴县| 额敏县| 寿阳县| 光泽县| 寻乌县| 绩溪县| 栾城县| 湘西| 安顺市| 禄丰县| 筠连县| 义马市| 清流县| 新河县| 东乡| 射洪县| 南安市| 盖州市| 大竹县| 都昌县| 双辽市| 易门县| 阳春市| 新绛县| 广宁县| 桓台县| 佛学| 郑州市| 陇西县| 信丰县| 青龙| 井研县| 安新县| 无棣县| 临洮县| 星座|