[英]How to use/refector nested promises?
我在我的 TypeScript 應用程序中使用 Promises 從最好的來源獲取我的數據。 首先應該嘗試 localStorage,然后對我的服務器進行 API 調用。 我不確定這種方法是否是最好的,所以歡迎提出意見。
loadData(): Promise<void> {
return this.loadFromLocalStorage()
.catch(() => {
this.loadFromApi();
})
}
loadFromLocalStorage() : Promise<void> {
return new Promise((resolve, reject) => {
if( !this.isSupported(() => localStorage) ) reject("Localstorage is not supported");
data = JSON.parse( localStorage.getItem('metadata') );
if( !data || !data.date ) {console.log("no data"); reject("Not all data was present");}
this.setData(data, false).then(() => {resolve()});
});
}
loadFromApi() : Promise<void> {
return new Promise((resolve, reject) => {
this.http.get<{date?:string,ddi?:number,stoff?:number}>(this.apiUrl+'meta.json')
.subscribe(data => {
this.setData(data, true).then(() => resolve());
},
() => {
reject("API call failed");
});
});
}
setData(data, saveToLocal: boolean) : Promise<void> {
return new Promise((resolve) => {
let promises: Promise<Pilot|void>[] = [];
if (data.ddi) {
promises.push( this.pilotService.getById(data.ddi).then(ddi => this.ddi = ddi) );
}
// There's more promises by the way, this is simplified
Promise.all(promises).then(function() {
resolve();
});
});
}
現在我知道這里有一些錯誤,但主要問題是:我如何處理在承諾中返回承諾? 甚至有必要嗎? 我嘗試尋找一種解決方案來解決問題,但找不到一個考慮到當第一個承諾(從 localStorage 獲取)失敗時不會發生某些操作的解決方案。
讓我們繪制你的 api
loadData
/
/
/
*
loadFromLS ------* loadFromApi
\ /
\ /
\ /
\ /
\ /
\ /
* *
setData
所以你 api 是一個方向,(什么都沒有上升)
但是你的 loadData 在這里是不必要的,我也建議這種 api,它沒有經過測試,將其視為偽實現
waterfall([this.loadFromLS(this.loadFromApi), this.setData]
所以瀑布接受一系列的承諾,這些承諾在彼此之后調用並傳遞上一個到下一個承諾的結果
請參閱下面的完整偽演示
function waterfall (...args) {
const promises = [].concat(...args)
const first = promises[0]
let firstPromise = first()
for (let i = 1; i < promises.length; i++) {
firstPromise = firstPromise.then(() => promises[i])
}
return firstPromise
}
class MyClass {
loadData(): Promise<any> {
const data = waterfall([this.loadFromLS(this.loadFromApi), this.setData])
return Promise.resolve(data)
}
loadFromLocalStorage(replacement) {
return function (): Promise<void> {
if (!this.isSupported(() => localStorage) {
return replacement && replacement()
}
const data = JSON.parse(localStorage.getItem('metadata'))
return Promise.resolve(data)
}
}
loadFromApi() : Promise<void> {
return this.http.get<{date?:string,ddi?:number,stoff?:number}>(this.apiUrl+'meta.json')
}
setData(data) : Promise<void> {
let promises: Promise<Pilot|void>[] = [];
if (data.ddi) {
promises.push( this.pilotService.getById(data.ddi).then(ddi => this.ddi = ddi) );
}
// There's more promises by the way, this is simplified
return Promise.all(promises)
}
}
您發布的答案中的改進代碼很好,但仍然可以使其更加簡潔和可維護:
throw
異步函數會導致返回的 Promise 拒絕)await
作為處理await
ed Promise 拒絕的方法時,您可以使用try/catch
,它使代碼非常直觀async loadData(): Promise<void> {
try {
return await this.loadFromLocalStorage(); //await this one to catch rejections
} catch(e) {
console.log(e.message); //handle error
return this.loadFromApi(); //await or don't await this one, same result
}
}
async loadFromLocalStorage() : Promise<Pilot[]> {
if( !this.isSupported(() => localStorage) ) throw new Error("Not Supported");
//if JSON.parse errors, the promise will also convert the exception to a rejection
data = JSON.parse( localStorage.getItem('metadata') );
if( !data || !data.date ) throw new Error("No Data");
//this function returns a promise resolving with whatever `setData` resolves with
//or rejects if `setData` rejects
return this.setData(data, false);
}
//this one still needs to return a promise as it wraps a .subscribe()
loadFromApi() : Promise<(Pilot|void)[]> {
return new Promise((resolve, reject) => {
this.http.get<{date?:string,ddi?:number,stoff?:number}>(this.apiUrl+'meta.json')
.subscribe( data => {
resolve(this.setData(data, true)); //resolve with the data
},
reject); //if there's an error, just reject with the error
});
}
async setData(data, saveToLocal: boolean) : Promise<(Pilot|void)[]> {
let promises: Promise<Pilot|void>[] = [];
if (data.ddi) {
promises.push( this.pilotService.getById(data.ddi).then(ddi => this.ddi = ddi) );
}
//Promise.all will reject if any one of the inner promises rejects
//To circumvent this, add a .catch() to the inner promises
return Promise.all(promises);
}
此代碼遵循 Promises 和 async/await 的更慣用用法。 每個函數要么在成功時以有意義的值解析,要么在因任何原因失敗時以有意義的錯誤拒絕。
由於 async/await,任何錯誤也會導致拒絕“冒泡”堆棧一直到調用者,即如果loadFromApi()
出現問題,Promise 將拒絕並返回錯誤,然后導致loadData()
也拒絕相同的錯誤,將其暴露給調用loadData()
的函數,並允許您封裝此數據 API 的錯誤處理(由任何內部函數引起的任何錯誤都可以“冒泡”到入口點loadData()
並隨心所欲處理)
為了解決這個問題,進行了三處更改:
resolve()
/ reject()
被resolve(true)
/ resolve(false)
取代resolve
/ reject
后調用return
以停止進一步執行.catch
.then()
/ .catch
被async
/ await
取代這是我的最終代碼:
loadData(): Promise<void> {
return new Promise(async (resolve, reject) => {
if( await this.loadFromLocalStorage() ) {
resolve();
return
}
await this.loadFromApi();
resolve()
});
}
loadFromLocalStorage() : Promise<boolean> {
return new Promise(async (resolve, reject) => {
if( !this.isSupported(() => localStorage) ) {resolve(false); return;}
data = JSON.parse( localStorage.getItem('metadata') );
if( !data || !data.date ) {console.log("no data"); resolve(false); return;}
await this.setData(data, false);
resolve(true);
});
}
loadFromApi() : Promise<boolean> {
return new Promise(async (resolve, reject) => {
this.http.get<{date?:string,ddi?:number,stoff?:number}>(this.apiUrl+'meta.json')
.subscribe( async data => {
await this.setData(data, true)
resolve(true);
},
() => {
resolve(false);
});
});
}
setData(data, saveToLocal: boolean) : Promise<null> {
return new Promise((resolve) => {
let promises: Promise<Pilot|void>[] = [];
if (data.ddi) {
promises.push( this.pilotService.getById(data.ddi).then(ddi => this.ddi = ddi) );
}
// There's more promises by the way, this is simplified
Promise.all(promises).then(function() {
resolve();
});
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.