fp-ts 如何处理管道内的异步操作
fp-ts How to Handle Async operations within pipe
我正在学习 fp-ts 并且想知道如何更好地组织我的函数以避免嵌套折叠。我在网上看到的所有示例都有一个很好的流线型 pipe 函数调用,但我不知道如何避免嵌套折叠。
一些上下文 - 在较高级别,此代码的目的是创建一个 Location,如果成功,则创建一个 Station.如果任何一个操作失败,return 返回一个适当的错误给调用者。如果一切顺利,return一个201。
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
//use the same value for now
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
const location: E.Either<ApiError, LocationDTO> = await this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
);
const stationName: string = StationService.retailOnlineStationName(site);
pipe(
location,
E.fold(
(err: ApiError) => ConfigController.respondWithError(err, res),
async (loc: LocationDTO) => {
pipe(
await this.stationService.createStation(site, stationName, loc.id),
E.fold(
(err: ApiError) => ConfigController.respondWithError(err, res),
(_: StationDTO) => res.status(201).send()
)
);
}
)
);
}
static respondWithError(err: ApiError, res: Response) {
res.status(err.statusCode).json(err);
}
想象一下我们正在使用 Promise
,代码会是什么样子?您将使用 .then
链接所有良好案例处理代码,并仅附加一个带有最终 .catch
.
的不良案例处理程序
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
const stationName: string = StationService.retailOnlineStationName(site);
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
// for illustration purpose, we suppose here
// the service returns a Promise of actual value
// instead of Promise of Either
await this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
).then((loc: LocationDTO) => {
return this.stationService.createStation(site, stationName, loc.id)
}).then((_: StationDTO) => {
res.status(201).send()
}).catch(err => {
ConfigController.respondWithError(err, res),
})
}
fp 版本应该具有相同的结构,只是类型不同。我们可以使用 TaskEither
类型来建模 Promise
.
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
const stationName: string = StationService.retailOnlineStationName(site);
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
// here the service shall return Promise of Either
const createLocationTask = () => this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
)
const chainedTask = pipe(
createLocationTask,
TE.fold(
TE.throwError, // pass through error
(loc: LocationDTO) => async () => stationService.createStation(site, stationName, loc.id),
),
TE.fold(
// catch error
(err: ApiError) => async () => ConfigController.respondWithError(err, res),
(_: StationDTO) => async () => { res.status(201).send() },
)
)
await chainedTask()
}
附件是带有存根的 ts playground 演示。
我正在学习 fp-ts 并且想知道如何更好地组织我的函数以避免嵌套折叠。我在网上看到的所有示例都有一个很好的流线型 pipe 函数调用,但我不知道如何避免嵌套折叠。
一些上下文 - 在较高级别,此代码的目的是创建一个 Location,如果成功,则创建一个 Station.如果任何一个操作失败,return 返回一个适当的错误给调用者。如果一切顺利,return一个201。
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
//use the same value for now
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
const location: E.Either<ApiError, LocationDTO> = await this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
);
const stationName: string = StationService.retailOnlineStationName(site);
pipe(
location,
E.fold(
(err: ApiError) => ConfigController.respondWithError(err, res),
async (loc: LocationDTO) => {
pipe(
await this.stationService.createStation(site, stationName, loc.id),
E.fold(
(err: ApiError) => ConfigController.respondWithError(err, res),
(_: StationDTO) => res.status(201).send()
)
);
}
)
);
}
static respondWithError(err: ApiError, res: Response) {
res.status(err.statusCode).json(err);
}
想象一下我们正在使用 Promise
,代码会是什么样子?您将使用 .then
链接所有良好案例处理代码,并仅附加一个带有最终 .catch
.
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
const stationName: string = StationService.retailOnlineStationName(site);
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
// for illustration purpose, we suppose here
// the service returns a Promise of actual value
// instead of Promise of Either
await this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
).then((loc: LocationDTO) => {
return this.stationService.createStation(site, stationName, loc.id)
}).then((_: StationDTO) => {
res.status(201).send()
}).catch(err => {
ConfigController.respondWithError(err, res),
})
}
fp 版本应该具有相同的结构,只是类型不同。我们可以使用 TaskEither
类型来建模 Promise
.
public async initialize(
@requestParam('site') site: string,
@request() req: Request,
@response() res: Response
) {
const stationName: string = StationService.retailOnlineStationName(site);
const nameAndPublicId = LocationService.retailOnlineLocationName(site);
// here the service shall return Promise of Either
const createLocationTask = () => this.locationService.createLocation(
site,
nameAndPublicId,
nameAndPublicId
)
const chainedTask = pipe(
createLocationTask,
TE.fold(
TE.throwError, // pass through error
(loc: LocationDTO) => async () => stationService.createStation(site, stationName, loc.id),
),
TE.fold(
// catch error
(err: ApiError) => async () => ConfigController.respondWithError(err, res),
(_: StationDTO) => async () => { res.status(201).send() },
)
)
await chainedTask()
}
附件是带有存根的 ts playground 演示。