[英]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.