blog
blog copied to clipboard
async/await配合for循环的异步串行请求
问题: 现有模块id
数组moduleIds
,通过模块id
请求模块数据,并且,后一个模块的接口请求依赖前一个模块的数据。当任意一个接口请求失败,则终止后续的请求。
实现一个函数,返回结果包含请求成功模块的数据和请求失败模块的错误数据。
interface IApiResponse {
message: string;
errorCode: number;
dataBePassedToNextModule?: string;
}
interface IApiResponseWithId extends IApiResponse {
id: number;
}
interface IExtraData {
info: string;
}
interface INgOnInit {
ngOnInit(): void;
}
interface IViewModel {
id: number;
message: string;
}
interface IViewModelById {
[id: number]: IViewModel;
}
const coin = (): boolean => Math.random() < 0.5;
const apiErrorSwitch: string = 'on';
//模拟传递给下一次模块请求的依赖数据
const mockDatasBePassedToNextModule: string[] = ['react', 'angular', 'jquery'];
//模拟api请求
const fetch = (id: number, data?: string | undefined): Promise<IApiResponse> => new Promise((resolve, reject) => {
const coinValue: boolean = coin();
let result: IApiResponse;
// console.log('coinValue: ', coinValue);
const baseMessage: string = `请求module:${id}数据成功`;
setTimeout(() => {
const successData: IApiResponse = {
message: data ? `${baseMessage}|${data}` : baseMessage,
errorCode: 0,
dataBePassedToNextModule: mockDatasBePassedToNextModule[id - 1]
};
const errorData: IApiResponse = {
message: `请求module:${id}数据失败`,
errorCode: -100
};
if (apiErrorSwitch === 'on') {
result = coinValue ? successData : errorData;
} else {
result = successData;
}
if (result.errorCode) {
reject(result);
}
resolve(successData);
}, 1000);
});
//fetch一个模块
const fetchModule = async (id: number, data?: string | undefined): Promise<IApiResponseWithId> => {
try {
const result: IApiResponse = await fetch(id, data);
const resultWithId: IApiResponseWithId = Object.assign({ id }, result);
return resultWithId;
} catch (errObj) {
errObj.id = id;
delete errObj.errorCode;
errObj.extraData = { info: '前端附加错误信息' };
throw errObj;
}
};
function fetchFirstModule(idx: number): boolean {
return idx === 0;
}
//fetch多个模块
async function fetchModulesByIds(modIds: number[]): Promise<IViewModelById> {
const moduleById: IViewModelById = {};
let module: IApiResponseWithId, dataBePassedToNextModule: string | undefined;
let error: any;
for (let i: number = 0, moduleIdCount: number = modIds.length; i < moduleIdCount; i++) {
const moduleId: number = modIds[i];
if (error) break;
try {
console.log(Date.now());
if (dataBePassedToNextModule || fetchFirstModule(i)) {
module = await fetchModule(moduleId, dataBePassedToNextModule);
dataBePassedToNextModule = module.dataBePassedToNextModule;
const vm: IViewModel = getViewModelByModule(module);
moduleById[vm.id] = vm;
}
} catch (err) {
error = err;
moduleById[err.id] = err;
}
}
return moduleById;
}
function getViewModelByModule(mod: IApiResponseWithId): IViewModel {
const vm: IViewModel = { id: -1, message: '' };
vm.id = mod.id;
vm.message = mod.message;
return vm;
}
class Component implements INgOnInit {
public vm: any = {
1: {},
2: {},
3: {}
};
private moduleIds: number[] = [1, 2, 3];
constructor() {
this.ngOnInit();
}
public async ngOnInit() {
//fetch一个模块
try {
const moduleOne = await fetchModule(1);
console.log('moduleOne: ', moduleOne);
} catch (e) {
console.log('moduleOne error:', e);
}
//fetch多个模块
const moduleById = await fetchModulesByIds(this.moduleIds);
this.vm = Object.assign(this.vm, moduleById);
console.log('render vm: ', this.vm);
}
}
const myFuckingComponent = new Component();
export { };
当然,服务器端API
接口,可以改造成或者新定义一个接收模块id数组作为参数的接口,这样比较灵活,不论前端需要一个模块,还是多个模块,只需要给定对应的模块id数组即可。前端的场景根据产品的要求可谓是多种多样,例如,首页模块比较多,所有模块都要懒加载;或者是首次请求加载3个模块,后续模块懒加载等。