iT邦幫忙

0

[Javascript] 非同步執行,如何延緩後面程式的處理 ??

  • 分享至 

  • xImage

版上的大家下午安,有個問題思考很久
搞不定/images/emoticon/emoticon06.gif
我目前開發一個功能 一鍵下載附檔。
將網頁上的附檔,透過一個按鈕可以全部下載下來。

有用到AJAX服務,設定檔案名稱。 ajax_WmsAccessor.setAttachmentFileName
組成檔案URL後,執行下載檔案的功能函式openSoure(tSourceFileUrl)
似乎下載太快,三個檔案的名稱都一樣...檔案大小不一樣。
試過加入alert() 延緩處理的程序,
下載的檔案就完全正確。

請教高手們這個小問題,我可以如何處理,讓AJAX服務執行完 再跑後面的下載。
謝謝大家。

DWREngine.setAsync(false); 
   if (tResultArray.length>0)
  { 
    for(var i=0;i<tResultArray.length;i++)
        {
         ajax_WmsAccessor.setAttachmentFileName(tResultArray[i][2]); //設定附檔名稱。
         var tSourceFileUrl = "/NaNaWeb/DownloadFile/"+tResultArray[i][0]+";"+"?action=downloadDocument" + "&docFileName=" + encodeURIComponent(tResultArray[i][0]);
         openSoure(tSourceFileUrl);     //下載檔案。     
         alert(tSourceFileUrl);
         }
   }
}

//下載原始檔
		function openSoure(pSourceFileUrl) {
			var iframe = document.createElement('iframe');
			iframe.src = pSourceFileUrl;
			iframe.setAttribute("class", "download");
			iframe.style.display = "none";
			document.body.appendChild(iframe);
			cleanDownload();	
		}

這是網頁上要下載的三個檔案:
https://ithelp.ithome.com.tw/upload/images/20251210/20105722DrhFkR3cad.png
將alert() 拿掉後,下載的檔案名稱都同一個....
https://ithelp.ithome.com.tw/upload/images/20251210/201057229onK5TdbDI.png

若有alert()延緩處理時間,檔案下載都成功。
https://ithelp.ithome.com.tw/upload/images/20251210/201057229FhPJUPxsX.png

淺水員 iT邦大師 6 級 ‧ 2025-12-10 15:53:06 檢舉
cleanDownload 的內容是什麼?
恩恩 iT邦新手 5 級 ‧ 2025-12-10 16:36:35 檢舉
//下載原始檔
function openSoure(pSourceFileUrl) {
var iframe = document.createElement('iframe');
iframe.src = pSourceFileUrl;
iframe.setAttribute("class", "download");
iframe.style.display = "none";
document.body.appendChild(iframe);
cleanDownload();
}

//清除下載連結
function cleanDownload(){
//jBPM(".download").attr("src", "");
return;
}
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
allenlwh
iT邦高手 1 級 ‧ 2025-12-10 14:32:55

提供一個曾經的解法,讓您參考看看。

目的:加入alert() 延緩處理的程序
方向:讓系統等待個2秒就好了
Javascript我不熟,大致方向就是系統停個2秒鐘。

setTimeout(function() {
  console.log("This message appears after 2 second.");
}, 2000);
恩恩 iT邦新手 5 級 ‧ 2025-12-10 14:56:43 檢舉

謝謝你的回覆,我加到10秒,只有一個檔案名稱被改掉,另兩個檔案名稱還是錯的/images/emoticon/emoticon02.gif

allenlwh iT邦高手 1 級 ‧ 2025-12-10 16:27:36 檢舉

若有alert()延緩處理時間,檔案下載都成功。

1.維持alert(tSourceFileUrl);
2.跳出的alert,由Javascript自行關閉

恩恩 iT邦新手 5 級 ‧ 2025-12-10 16:34:16 檢舉

2.跳出的alert,由Javascript自行關閉
這要怎麼自行關閉??

0
hoaohoao
iT邦新手 4 級 ‧ 2025-12-10 16:01:48

ajax_WmsAccessor.setAttachmentFileName
如果只是為了設定附檔名稱,為什麼要用非同步的ajax?

如果一定要用,就先把附檔名稱都準備好(確定你要的名稱都ok了),再來進行下載。

0
harry xie
iT邦研究生 1 級 ‧ 2025-12-10 16:49:59

這種一次下載多個檔案的功能我常做,常用做法都是用 Promise.all()

簡單講一下做法,你可以透過 AI 問更詳細的資訊。

因為你現在截圖的畫面上有呈現所有檔案的名稱了,那我就假設你可以把所有檔名存成一個陣列,

然後把陣列每個元素做 promisify,這樣就生成一個多個 Promise 物件的陣列,

然後用 Promise.all() 一次 call 下載檔案的 API 即可。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>Promise.all 批量下載檔案</title>
</head>
<body>
    <h1>批量下載檔案範例</h1>
    <button id="downloadButton">開始下載選定的檔案</button>

    <script>
        // 預先準備的檔名列表
        const fileNames = [
            'document_A.pdf',
            'image_B.jpg',
            'data_C.xlsx'
        ];

        // 模擬的 API 呼叫函式
        // 實際應用中,你需要替換成你的後端 API 呼叫
        /**
         * 模擬從 API 下載一個檔案
         * @param {string} fileName 要下載的檔案名稱
         * @returns {Promise<{fileName: string, blob: Blob}>} 包含檔名和 Blob 資料的 Promise
         */
        function downloadFileFromApi(fileName) {
            console.log(`正在請求檔案: ${fileName}`);

            // 實際應用中,這裡會是 fetch 或 XMLHttpRequest
            return fetch(`/api/download?file=${fileName}`, {
                method: 'GET',
                // 這裡可以加上必要的 headers,例如授權 token
            })
            .then(response => {
                // 檢查 HTTP 狀態碼
                if (!response.ok) {
                    throw new Error(`API 請求失敗,狀態碼: ${response.status} (${response.statusText})`);
                }
                // 假設 API 返回的是檔案的 Blob 資料
                return response.blob();
            })
            .then(blob => {
                // 返回一個包含檔名和 Blob 資料的物件
                return { fileName: fileName, blob: blob };
            })
            .catch(error => {
                // 處理網路或 API 錯誤
                console.error(`下載 ${fileName} 時發生錯誤:`, error);
                // 即使失敗,也返回一個可以被 Promise.all 處理的結果
                // 可以在這裡返回一個特定的失敗標記,如果 Promise.all 必須全部成功才繼續
                // 如果需要「任一失敗則整體失敗」,則直接讓錯誤繼續拋出
                throw error; // 讓 Promise.all 進入 catch 區塊
            });
        }

        // 建立下載連結並觸發下載的輔助函式
        /**
         * 使用 Blob 資料在瀏覽器中建立下載連結並觸發下載
         * @param {string} fileName 下載的檔案名稱
         * @param {Blob} blob 檔案的 Blob 資料
         */
        function triggerDownload(fileName, blob) {
            // 創建一個指向 Blob 資料的 URL
            const url = window.URL.createObjectURL(blob);
            
            // 創建一個隱藏的 <a> 元素
            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = url;
            // 設置下載的檔名
            a.download = fileName; 
            
            // 將元素添加到 DOM 並點擊它以觸發下載
            document.body.appendChild(a);
            a.click();
            
            // 清理:釋放創建的 URL 並移除元素
            window.URL.revokeObjectURL(url);
            document.body.removeChild(a);
        }

        // 處理批量下載的主函式
        async function batchDownloadFiles() {
            const button = document.getElementById('downloadButton');
            button.disabled = true; // 下載期間禁用按鈕
            button.textContent = '正在下載...';

            try {
                // 1. 建立 Promise 陣列
                // 對每個檔名呼叫 downloadFileFromApi,並將返回的 Promise 收集起來
                const downloadPromises = fileNames.map(fileName => downloadFileFromApi(fileName));

                // 2. 使用 Promise.all 併發執行所有下載 Promise
                // 它會等待所有 Promise 都成功 resolve
                const results = await Promise.all(downloadPromises);

                // 3. 處理下載結果
                console.log('所有檔案下載完成,準備觸發下載。');

                // results 是一個陣列,包含每個檔案的 {fileName, blob} 物件
                results.forEach(result => {
                    // 觸發每個檔案的下載
                    triggerDownload(result.fileName, result.blob);
                });

                alert(`成功下載 ${results.length} 個檔案!請檢查您的下載資料夾。`);

            } catch (error) {
                // 如果 Promise.all 中任一個 Promise 發生 rejected,就會進入這裡
                console.error('批量下載過程中發生錯誤:', error);
                alert(`批量下載失敗。請查看控制台瞭解詳情。錯誤: ${error.message}`);
            } finally {
                // 無論成功或失敗,最後都恢復按鈕狀態
                button.disabled = false;
                button.textContent = '開始下載選定的檔案';
            }
        }

        // 為按鈕添加事件監聽器
        document.getElementById('downloadButton').addEventListener('click', batchDownloadFiles);

        // 由於瀏覽器安全限制,直接運行這個程式碼時,
        // 模擬的 /api/download 請求會失敗(因為沒有這個端點),
        // 如果想在本地測試下載流程,可以將 downloadFileFromApi 替換為一個返回假 Blob 的函式。
        
        // --- 替換為模擬成功的函式(用於本地測試下載流程) ---
        /*
        function downloadFileFromApi(fileName) {
             console.log(`正在模擬請求檔案: ${fileName}`);
             return new Promise(resolve => {
                 setTimeout(() => { // 模擬網路延遲
                     // 創建一個假的 Blob 資料
                     const mockContent = `這是 ${fileName} 檔案的內容。`;
                     const mockBlob = new Blob([mockContent], { type: 'text/plain' });
                     resolve({ fileName: fileName, blob: mockBlob });
                 }, 500 + Math.random() * 500);
             });
        }
        */
        // ---------------------------------------------------
    </script>
</body>
</html>

我要發表回答

立即登入回答