使用 Promise.all() 优化多个 api 请求
Optimise multiple api requests with Promise.all()
我正在尝试在我的 NestJS API 中优化对第 3 方 API (Spotify) 的多个制作。
我的代码可以运行,但我找不到优化它的方法。
@Get('')
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
const user = this.userService.getCurrentUserProfile(token);
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
const getUserTopData = async () => {
const promise = [];
types.forEach((type) => {
periods.forEach(async (period) => {
promise.push(
this.personalizationService.getUserTopArtistsTracks(
type as 'artists' | 'tracks',
{
time_range: period as
| 'short_term'
| 'medium_term'
| 'long_term',
limit: 1,
},
token,
),
);
});
});
return promise;
};
const promises = await getUserTopData();
const [
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
] = promises;
return Promise.all([
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
])
.then((values) => {
const user = values[0];
const artistsShortTerm = values[1];
const artistsMediumTerm = values[2];
const artistsLongTerm = values[3];
const tracksShortTerm = values[4];
const tracksMediumTerm = values[5];
const tracksLongTerm = values[6];
return {
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
};
})
.catch((err) => {
return err;
});
}
this.personalizationService.getUserTopArtistsTracks() 执行请求的函数:
export class PersonalizationService {
constructor(private httpService: HttpService) {}
async getUserTopArtistsTracks(
type: 'artists' | 'tracks',
query: {
time_range: 'short_term' | 'medium_term' | 'long_term';
limit: number;
offset?: number;
},
token: string,
): Promise<SpotifyApi.UsersTopArtistsResponse> {
const topArtists = this.httpService
.get<SpotifyApi.UsersTopArtistsResponse>(
`https://api.spotify.com/v1/me/top/${type}?${qs.stringify(query)}`,
{
headers: { Authorization: `Bearer ${token}` },
},
)
.toPromise();
return (await topArtists).data;
}
}
在 NestJS 上调用我的控制器 return 像这样 JSON :
我觉得我的代码太长了,有更短的方法可以做到。但是我不知道怎么办。
如果不需要,则无需创建 promise 函数,您可以 return 为异步函数创建 promise。不要将 async
关键字用于同步功能。
@Get('')
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
const getUserTopData = () => {
const promise = [];
types.forEach((type) => {
periods.forEach((period) => {
promise.push(
this.personalizationService.getUserTopArtistsTracks(
type as 'artists' | 'tracks',
{
time_range: period as
| 'short_term'
| 'medium_term'
| 'long_term',
limit: 1,
},
token,
),
);
});
});
return promise;
};
const [
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
] = await Promise.all([
this.userService.getCurrentUserProfile(token),
...getUserTopData()
]);
return {
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
};
}
当您使用 Promise.all
时,您已经在并行执行异步请求。 除非 spotify 让您以更少的 API 请求获得更多数据,否则您将找不到更好的解决方案,因为您需要查询 Spotify。
不过,代码clarity/quality可以改进:
- 控制器不应处理逻辑
因此将您的控制器重定向到服务 >
@Get("")
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
// even that can be within the function
const user = this.userService.getCurrentUserProfile(token);
return await this.userService.getUserTopData(user, token);
}
然后在您的服务中,直接在常量中定义类型。
async getUserTopData(user: User, token: string) {
try {
const periods: ("short_term" | "medium_term" | "long_term")[] = ["short_term", "medium_term", "long_term"];
const types: ("artists" | "tracks")[] = ["artists", "tracks"];
const promises = [];
types.forEach(type =>
periods.forEach(period =>
promises.push(
this.personalizationService.getUserTopArtistsTracks(type, { timeRange: period, limit: 1 }, token)
),
),
);
promises.push(this.getCurrentUserProfile(token));
const [
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
] = await Promise.all(promises);
return {
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
};
} catch (e) {
// Do something here
}
}
处理潜在错误(try、catch、异常处理程序...)
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
// line up promises in an array
const getUserTopData = () =>
types.flatMap((type) =>
periods.map((period) =>
this.personalizationService.getUserTopArtistsTracks(/* ... */)
)
)
);
// wait for promises (result order will reflect array order)
const results = await Promise.all(getUserTopData());
// give the result list back its structure
return types.reduce((obj, type) => {
obj[type] = periods.reduce((obj, period) => {
obj[period] = results.shift();
return obj;
}, {});
return obj;
}, {});
这将产生这样的数据结构:
{
short_term: {artists: '...', tracks: '...'},
medium_term: {artists: '...', tracks: '...'},
long_term: {artists: '...', tracks: '...'}
}
随意以不同方式解压 results
数组。
我正在尝试在我的 NestJS API 中优化对第 3 方 API (Spotify) 的多个制作。 我的代码可以运行,但我找不到优化它的方法。
@Get('')
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
const user = this.userService.getCurrentUserProfile(token);
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
const getUserTopData = async () => {
const promise = [];
types.forEach((type) => {
periods.forEach(async (period) => {
promise.push(
this.personalizationService.getUserTopArtistsTracks(
type as 'artists' | 'tracks',
{
time_range: period as
| 'short_term'
| 'medium_term'
| 'long_term',
limit: 1,
},
token,
),
);
});
});
return promise;
};
const promises = await getUserTopData();
const [
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
] = promises;
return Promise.all([
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
])
.then((values) => {
const user = values[0];
const artistsShortTerm = values[1];
const artistsMediumTerm = values[2];
const artistsLongTerm = values[3];
const tracksShortTerm = values[4];
const tracksMediumTerm = values[5];
const tracksLongTerm = values[6];
return {
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
};
})
.catch((err) => {
return err;
});
}
this.personalizationService.getUserTopArtistsTracks() 执行请求的函数:
export class PersonalizationService {
constructor(private httpService: HttpService) {}
async getUserTopArtistsTracks(
type: 'artists' | 'tracks',
query: {
time_range: 'short_term' | 'medium_term' | 'long_term';
limit: number;
offset?: number;
},
token: string,
): Promise<SpotifyApi.UsersTopArtistsResponse> {
const topArtists = this.httpService
.get<SpotifyApi.UsersTopArtistsResponse>(
`https://api.spotify.com/v1/me/top/${type}?${qs.stringify(query)}`,
{
headers: { Authorization: `Bearer ${token}` },
},
)
.toPromise();
return (await topArtists).data;
}
}
在 NestJS 上调用我的控制器 return 像这样 JSON :
我觉得我的代码太长了,有更短的方法可以做到。但是我不知道怎么办。
如果不需要,则无需创建 promise 函数,您可以 return 为异步函数创建 promise。不要将 async
关键字用于同步功能。
@Get('')
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
const getUserTopData = () => {
const promise = [];
types.forEach((type) => {
periods.forEach((period) => {
promise.push(
this.personalizationService.getUserTopArtistsTracks(
type as 'artists' | 'tracks',
{
time_range: period as
| 'short_term'
| 'medium_term'
| 'long_term',
limit: 1,
},
token,
),
);
});
});
return promise;
};
const [
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
] = await Promise.all([
this.userService.getCurrentUserProfile(token),
...getUserTopData()
]);
return {
user,
artistsShortTerm,
artistsMediumTerm,
artistsLongTerm,
tracksShortTerm,
tracksMediumTerm,
tracksLongTerm,
};
}
当您使用 Promise.all
时,您已经在并行执行异步请求。 除非 spotify 让您以更少的 API 请求获得更多数据,否则您将找不到更好的解决方案,因为您需要查询 Spotify。
不过,代码clarity/quality可以改进:
- 控制器不应处理逻辑
因此将您的控制器重定向到服务 >
@Get("")
async getAllData(@Req() req: Request) {
const token = req.cookies.access_token;
// even that can be within the function
const user = this.userService.getCurrentUserProfile(token);
return await this.userService.getUserTopData(user, token);
}
然后在您的服务中,直接在常量中定义类型。
async getUserTopData(user: User, token: string) { try { const periods: ("short_term" | "medium_term" | "long_term")[] = ["short_term", "medium_term", "long_term"]; const types: ("artists" | "tracks")[] = ["artists", "tracks"]; const promises = []; types.forEach(type => periods.forEach(period => promises.push( this.personalizationService.getUserTopArtistsTracks(type, { timeRange: period, limit: 1 }, token) ), ), ); promises.push(this.getCurrentUserProfile(token)); const [ user, artistsShortTerm, artistsMediumTerm, artistsLongTerm, tracksShortTerm, tracksMediumTerm, tracksLongTerm, ] = await Promise.all(promises); return { user, artistsShortTerm, artistsMediumTerm, artistsLongTerm, tracksShortTerm, tracksMediumTerm, tracksLongTerm, }; } catch (e) { // Do something here } }
处理潜在错误(try、catch、异常处理程序...)
const periods = ['short_term', 'medium_term', 'long_term'];
const types = ['artists', 'tracks'];
// line up promises in an array
const getUserTopData = () =>
types.flatMap((type) =>
periods.map((period) =>
this.personalizationService.getUserTopArtistsTracks(/* ... */)
)
)
);
// wait for promises (result order will reflect array order)
const results = await Promise.all(getUserTopData());
// give the result list back its structure
return types.reduce((obj, type) => {
obj[type] = periods.reduce((obj, period) => {
obj[period] = results.shift();
return obj;
}, {});
return obj;
}, {});
这将产生这样的数据结构:
{
short_term: {artists: '...', tracks: '...'},
medium_term: {artists: '...', tracks: '...'},
long_term: {artists: '...', tracks: '...'}
}
随意以不同方式解压 results
数组。