在Async/Await中優(yōu)雅地終止回調(diào)函數(shù)
使用async/await進(jìn)行異步操作時(shí),如何安全地從一個(gè)執(zhí)行時(shí)間不確定的回調(diào)函數(shù)中退出,是一個(gè)常見挑戰(zhàn)。本文將針對(duì)一個(gè)場(chǎng)景,演示如何在async/await環(huán)境下有效控制回調(diào)函數(shù)的退出。
問題: 代碼使用MutationObserver監(jiān)聽按鈕屬性變化,屬性變化時(shí)需要退出異步函數(shù)。promise的傳統(tǒng)寫法易于實(shí)現(xiàn),但代碼復(fù)雜時(shí),嵌套的Promise會(huì)降低可讀性和可維護(hù)性。目標(biāo)是將代碼改寫為async/await風(fēng)格,同時(shí)解決如何在回調(diào)函數(shù)中安全退出異步函數(shù)的問題。
示例代碼:
// 使用Promise的傳統(tǒng)寫法 const a = (): Promise<void> => { return new Promise((resolve) => { const callback = (mutations: MutationRecord[]) => { // 監(jiān)聽按鈕屬性變化,在此處退出函數(shù) resolve(); }; const observer = new MutationObserver(callback); observer.observe(buttonEl, { attributes: true }); // 調(diào)用Promise函數(shù),成功后觸發(fā)按鈕點(diǎn)擊事件,然后監(jiān)聽屬性變化 p().then(() => { buttonEl.click(); }); }); }; // 使用async/await的寫法 const b = async (): Promise<void> => { const callback = (mutations: MutationRecord[]) => { // 監(jiān)聽按鈕屬性變化,如何在此處退出函數(shù)? }; const observer = new MutationObserver(callback); observer.observe(buttonEl, { attributes: true }); // 調(diào)用Promise函數(shù),成功后觸發(fā)按鈕點(diǎn)擊事件,然后監(jiān)聽屬性變化 await p(); buttonEl.click(); };
解決方案:
為了在async/await中實(shí)現(xiàn)與a函數(shù)相同的功能,可以使用Promise.withResolvers()方法。此方法返回一個(gè)包含Promise和resolve函數(shù)的對(duì)象。
改進(jìn)后的代碼:
const b = async (): Promise<void> => { const { promise, resolve } = Promise.withResolvers<void>(); const callback = (mutations: MutationRecord[]) => { resolve(); // 在回調(diào)函數(shù)中調(diào)用resolve()來解決Promise }; const observer = new MutationObserver(callback); observer.observe(buttonEl, { attributes: true }); await p(); buttonEl.click(); await promise; // 等待Promise解決 };
兼容性說明:
Promise.withResolvers()目前處于提案階段,并非所有瀏覽器都原生支持。為了保證兼容性,可能需要使用polyfill,例如core-JS。 同樣,typescript用戶需要更新到5.4或更高版本,并在tsconfig.json中配置lib包含esnext。
通過Promise.withResolvers(),可以在回調(diào)函數(shù)中調(diào)用resolve()來解決Promise,從而有效地控制async/await函數(shù)的退出流程,避免復(fù)雜的Promise嵌套,提高代碼的可讀性和可維護(hù)性。