JS 非同步作業串接之 jQuery 與香草寫法 ft. Github Copilot
2 |
維護古蹟過程遇到要串接多個非同步作業的需求,例如:Warn()、UpdateConfirm() 與 CallApi() 為非同步作業(詢問使用者確認或取消、呼叫 WebAPI),函式有點年紀故傳回的是 jQuery Promise,我要做到 Warn() 確定才呼叫 UpdateConfirm(),UpdateConfirm() 才呼叫 CallApi()。這不是什麼新鮮需求,翻到 11 年前寫的文章 - 以 jQuery 循序執行 AJAX 呼叫,並依結果決定是否繼續 準備抄 Code,卻發現它過期了。文章用的 jQuery deferred.pipe() 方法,早在 jQuery 1.8 版就宣告棄用,建議改用 deferred.then()。
為了與時俱進,今天來補上新寫法。以下是個簡單範例,假設有 step1 到 step3 三個非同步方法,它們都會傳回 deferred.promise(),方便後續串 then()、done()、fail();非同步操作我借用 SweetAlert2 彈出對話框詢問繼續或中止,三次詢問都答 OK 才算完成,任一次按 STOP 就中止。
另外,我再增加了 step1() 傳資料給 step2()、step2() 傳資料給 step3() 的橋段,以貼近實務上可能出現的需求。主要串接寫法如下,還算好懂,step1().then((r) => step2(r)) 代表 step1() 若成功(其中呼叫 deferred.resolve()) 才執行 step2(),而 r 可承接其 deferred.resolve(x) 傳入的 x。
log('START');
step1()
.then((r) => step2(r))
.then((r) => step3(r))
.then(() => log('END'))
.fail(() => log('CANCEL'));
完整程式碼如下,並附上線上展示。
<!DOCTYPE html>
<html>
<head>
<title>jQuery Chained Promise Demo</title>
</head>
<body>
<div id="msgs">
</div>
<script src="https://unpkg.com/jquery"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2"></script>
<script>
function showConfirm(title, msg) {
return new Promise((resolve, reject) => {
Swal.fire({
title: title,
text: msg,
icon: 'info',
showCancelButton: true,
confirmButtonText: 'OK',
cancelButtonText: 'STOP'
}).then((result) => {
if (result.value) {
resolve();
} else {
reject();
}
});
});
}
let log = (msg) => {
$("#msgs").append("<div>" + msg + "</div>");
}
var step1 = () => {
const dfd = jQuery.Deferred();
log('STEP 1');
showConfirm('STEP1', 'Go?')
.then(() => dfd.resolve('R1'))
.catch(() => dfd.reject());
return dfd.promise();
};
var step2 = (res) => {
const dfd = jQuery.Deferred();
log(`STEP 2 (${res})`);
showConfirm('STEP2', 'Go?')
.then(() => dfd.resolve('R2'))
.catch(() => dfd.reject());
return dfd.promise();
};
var step3 = (res) => {
const dfd = jQuery.Deferred();
log(`STEP 3 (${res})`);
showConfirm('STEP3', 'Go?')
.then(() => dfd.resolve())
.catch(() => dfd.reject());
return dfd.promise();
};
log('START');
step1()
.then((r) => step2(r))
.then((r) => step3(r))
.then(() => log('END'))
.fail(() => log('CANCEL'));
</script>
</body>
</html>
講到與時俱進,如果沒有舊系統的包袱,其實這個需求用純 JavaScript 就可以解決,不需動用 jQuery,所以研究一下香草 JS 怎麼寫也是必要的。而基本語法改寫這類小事,2023 起已是 AI 的主場,搞懂原理就好(之前有研究過 JavaScript Promise)不需親力親為,省下寶貴的腦力體力做更重要的事。
讓我們來呼叫會寫程式的阿拉丁神燈 - Github Copilot。
在 VSCode 選取整段 jQuery 程式碼,滑鼠右鍵選單 Copilot / Start Code Chat 或按快捷鍵 Ctrl+I:
許願 rewrite the code, replace jQuery.Deferred with Promise
(敲中文也行,打英文比較快,而 AI 十分聰明友善,能無視錯字漏寫文法錯誤,隨便寫它也看得懂)
送出後稍等幾秒鐘,Github Copilot 已將整段改寫好,看看若沒錯誤(這個步驟不能省,AI 產生結果還是要自己看過再放進作品,不然哪天怎麼死的都不知道),按下方的 Accept 接收鈕可將程式碼置換成 Copilot 改寫的版本。
或者你還可以繼續下指令請 Copilot 優化程式碼,像是 extract shared function from step1, step2, setp3
請它將 step1 ~ step3 相似部分抽取成共用函式。許願成功,Coplit 將重複程式碼提取成 confirmStep 函式。
Copilot 改寫後版本如下,這類簡單 JavaScript 程式若無意外,一般不用修改便能執行。線上展示
<!DOCTYPE html>
<html>
<head>
<title>jQuery Chained Promise Demo</title>
</head>
<body>
<div id="msgs">
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2"></script>
<script>
function showConfirm(title, msg) {
return new Promise((resolve, reject) => {
Swal.fire({
title: title,
text: msg,
icon: 'info',
showCancelButton: true,
confirmButtonText: 'OK',
cancelButtonText: 'STOP'
}).then((result) => {
if (result.value) {
resolve();
} else {
reject();
}
});
});
}
function log(msg) {
const msgs = document.querySelector("#msgs");
const div = document.createElement("div");
div.textContent = msg;
msgs.appendChild(div);
}
function confirmStep(step, message) {
return new Promise((resolve, reject) => {
log(`${step}`);
showConfirm(step, message)
.then(() => resolve())
.catch(() => reject());
});
}
var step1 = () => {
return new Promise((resolve, reject) => {
confirmStep('STEP 1', 'Go?')
.then(() => resolve('R1'))
.catch(() => reject());
});
};
var step2 = (res) => {
return new Promise((resolve, reject) => {
confirmStep(`STEP 2 (${res})`, 'Go?')
.then(() => resolve('R2'))
.catch(() => reject());
});
};
var step3 = (res) => {
return new Promise((resolve, reject) => {
confirmStep(`STEP 3 (${res})`, 'Go?')
.then(() => resolve())
.catch(() => reject());
});
};
log('START');
step1()
.then((r) => step2(r))
.then((r) => step3(r))
.then(() => log('END'))
.catch(() => log('CANCEL'));
</script>
</body>
</html>
2023 年,寫了幾十年的程式老人正在學習適應 AI 新時代。
Example of how to chain serveral async functions which return jQuery prmises and run then one by one.
Comments
# by 小黑
nice
# by Wen
與時俱進也是很不容易的能力