我正在嘗試下載一份報告,該報告每天根據對報告端點的第一次請求生成。
創建報告時,端點回傳一個HTTP 202.
我有以下代碼來處理一些錯誤和重定向,以及嘗試“休眠”60 秒,然后再繼續嘗試端點。不幸的是,告訴我下載完成的第二個控制臺日志從未被呼叫,盡管檔案確實下載成功并且檔案流關閉。
// Main function
run()
async function run() {
await getReport()
await processReport()
}
async function getReport() {
console.log(`Downloading ${reportFileName}`)
await downloadFile(url, reportFileName)
console.log(`Downloaded ${reportFileName} successfully.`) // This is never called?
}
async function downloadFile (url, targetFile) {
return await new Promise((resolve, reject) => {
https.get(url, async response => {
const code = response.statusCode ?? 0
if (code >= 400) {
return reject(new Error(response.statusMessage))
}
// handle redirects
if (code > 300 && code < 400 && !!response.headers.location) {
resolve(downloadFile(response.headers.location, targetFile))
return
}
// handle file creation pending
if (code == 202) {
console.log(`Report: ${reportFileName} is still being generated, trying again in ${timeToSleepMs/1000} seconds...`)
await sleep(timeToSleepMs)
resolve(downloadFile(url, targetFile))
return
}
// make download directory regardless of if it exists
fs.mkdirSync(outputPath, { recursive: true }, (err) => {
if (error) throw error;
});
// save the file to disk
const fileWriter = fs
.createWriteStream(`${outputPath}/${targetFile}`)
.on('finish', () => {
resolve({})
})
response.pipe(fileWriter)
}).on('error', error => {
reject(error)
})
})
}
最后我的睡眠功能:
let timeToSleepMs = (60 * 1000)
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
我很確定這與某種異步問題有關,因為這似乎總是我與 Node 的問題,但我不確定如何處理它。我只想獲取一個檔案并在本地下載,如果我得到一個HTTP 202. 如果有更好的方法,請告訴我!
tl;dr - 我如何正確處理等待HTTP 202回應變成HTTP 200檔案生成時的回應,然后在下載檔案后繼續執行代碼?
uj5u.com熱心網友回復:
await downloadFile(url, reportFileName)呼叫遞回函式并等待最外層呼叫的承諾得到解決。但是如果函式遞回地呼叫自己
await sleep(timeToSleepMs)
return downloadFile(url, targetFile)
這個最外層的承諾永遠不會得到解決。
將上面兩行替換為
await sleep(timeToSleepMs)
resolve(downloadFile(url, targetFile))
return
那么最外層 promise 的決議就是遞回呼叫的第二外層 promise 的決議,依此類推。
uj5u.com熱心網友回復:
Heiko 已經確定了問題所在:當您return從回呼中退出時,您永遠不會解決 promise。一般來說,為了避免此類錯誤,建議不要將功能的承諾和業務邏輯混在一起。僅在后者中使用async/ await,不要將async函式作為回呼傳遞。在你的情況下,那將是
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function httpsGet(url) {
return new Promise((resolve, reject) => {
https.get(url, resolve).on('error', reject);
});
}
function writeFile(path, readableStream) {
return new Promise((resolve, reject) => {
const fileWriter = fs
.createWriteStream(path)
.on('finish', resolve)
.on('error', reject);
readableStream.pipe(fileWriter);
});
}
然后你可以輕松地撰寫一個沒有任何回呼的簡單函式:
async function downloadFile (url, targetFile) {
const response = httpsGet(url);
const code = response.statusCode ?? 0
if (code >= 400) {
throw new Error(response.statusMessage);
}
// handle redirects
if (code > 300 && code < 400 && !!response.headers.location) {
return downloadFile(response.headers.location, targetFile)
}
// handle file creation pending
if (code == 202) {
console.log(`Report: ${reportFileName} is still being generated, trying again in ${timeToSleepMs/1000} seconds...`)
await sleep(timeToSleepMs)
return downloadFile(url, targetFile)
}
// make download directory regardless of if it exists
fs.mkdirSync(outputPath, { recursive: true });
// save the file to disk
await writeFile(`${outputPath}/${targetFile}`, response);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/533676.html
