iT邦幫忙

0

【JS】如何製作含有ajax功能的click once button套件?

  • 分享至 

  • xImage

大家好,
如果想要防止使用者連續按button傳送request的情況,
所以程式碼如下:

const button = (function (clicked) {
    return () => {
        if (!clicked) return;
        clicked = false
        let postData = new FormData()
        postData.append('key', 'this is value')
        try {
            fetch('http://XXXX'.postData)
                .then(res => res.json())
                .then(data => { })
                .catch(() => { })
                .finally(() => {
                    clicked = true
                })
        } catch {
            clicked = true
        }
    }
}(true))

為了精簡程式碼,所以想將click once的功能做成一個套件,可重複使用,大致如下,
fn要能適用於,同步與異步的function。

const fn = (anyFunction = () => { }) => {
    return new Promise((resolve, reject) => {
        try {
            anyFunction() //(a)
            resolve() //(b)
        } catch {
            reject()
        }
    })
}
const clickOnceBtn = (anyFunction = () => { }) => {
    (function (isClicked) {
        return async () => {
            if (!isClicked) return;
            isClicked = false
            await fn(anyFunction) //(c)
            isClicked = true //(d)
        }
    }(true))
}

想要應用的方式如下,

const theFetch = ()=>{
    let postData = new FormData()
    postData.append('key', 'this is value')
    fetch('http://XXXX',postData)
        .then(res => res.json())
        .then(data => { })
        .catch(() => { })
}
const button = clickOnceBtn(theFetch) //最後將button綁到onclick事件

目前卡在(c)還沒執行完,就執行(d)
原因是(b)在(a)還沒執行完就先回傳。
請問該怎麼修改比較好呢?
感謝

-----【解決方法】------
(1)放棄anyFunction帶入fn的作法,因為還需判斷是否有axios或fetch或promise。
(2)clickOnceBtn的架構維持不變。

const clickOnceBtn = (anyFunction) => {
    return (function (isClicked) {
        return async () => {
            if (isClicked) return;
            isClicked = true
            try {
                await anyFunction()
                //原本是 await fn(anyFunction)
            } finally {
                isClicked = false
            }
        }
    }(false))
}

(3)自行判斷function。
如果是普通function,const noFetch = ()=>console.log('')
如果有promise就必須這樣,

const theFetch = () => {
    return new Promise((resolve, reject) => {
        try {
            let postData = new FormData()
            postData.append('key', 'this is value')
            fetch('http://XXX', postData)
                .then(res => res.json())
                .then(data => { })
                .catch(() => { })
                .finally(() => resolve()) //resolve改到這裡
        } catch {
            reject()
        }
    })
}

(4)使用

const button1 = clickOnceBtn(theFetch)
const button2 = clickOnceBtn(noFetch)

提供大家參考,
也感謝大大們幫忙提供意見。

ps.忙了老半天,其實也才節省了三行。好像沒什麼必要這樣做
/images/emoticon/emoticon10.gif

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

0
rain_yu
iT邦新手 1 級 ‧ 2023-09-26 08:33:20
最佳解答

要解決這個問題,你可以將resolve延遲到async function完成執行後再呼叫。
以下是修改後的程式碼:

const fn = (anyFunction = () => { }) => {
  return new Promise((resolve, reject) => {
    try {
      const result = anyFunction(); // (a)
      if (result instanceof Promise) {
        result.then(() => {
          resolve(); // (b)
        }).catch(() => {
          reject();
        });
      } else {
        resolve(); // (b)
      }
    } catch {
      reject();
    }
  });
};

const clickOnceBtn = (anyFunction = () => { }) => {
  (function (isClicked) {
    return async () => {
      if (!isClicked) return;
      isClicked = false;

      try {
        await fn(anyFunction); // (c)
      } finally {
        isClicked = true; // (d)
      }
    };
  }(true));
};

這樣就可以確保在(async function (a)中的所有代碼執行完畢後,才會呼叫resolve (b)。

0
淺水員
iT邦大師 6 級 ‧ 2023-09-25 16:34:18
function triggerOnce(handler) {
    let busy = false;
    return async (event) => {
        if (busy) {
            return;
        }
        busy = true;
        try {
            await handler(event);
        } finally {
            busy = false;
        }
    }
}

document
    .querySelector('button')
    .addEventListener('click', triggerOnce(() => {
        // 要做的事
    }));

我要發表回答

立即登入回答