TypeScript Angular، چگونه میتوانم - به عنوان بخشی از زنجیرهای از Observables - عملیاتی را روی هر عضو یک آرایه اجرا کنم و فقط تعداد موفقیتها را برگردانم؟
من یک رکورد والد دارم که نیاز به ایجاد آن دارم، و سپس یک سری رکورد فرزند برای ایجاد دارم (ترتیب اهمیتی ندارد). سپس نیاز دارم یک عمل ثبت پیگیری انجام دهم.
من میدانم چگونه یک زنجیره از اقدامات منفرد را روی یک قابل مشاهده با نگاشت آنها به هم ایجاد کنم، بنابراین میتوانم این کار را انجام دهم:
-- یک رکورد والد ایجاد کنید، شناسه آن را برگردانید:
createParentRecord(parentInfo: parentType): Observable<number>-- یک رکورد فرزند ایجاد کنید، شناسه آن را بازگردانید
createChildRecord(childInfo: childType): Observable<number>-- ثبت ورود
logStuff(parentId: number, childId: number)-- تابع ترکیبی تمام کار را انجام میدهد و parentId را برمیگرداند
doTheThing(): Observable<number> {
/* ... */
let parentId: number;
let childId: number;
return createParent(parentInfo).pipe(
tap(id => (parentId = id)),
mergeMap(id => { childInfo.parentId = parentId; return createChildRecord(childInfo);} ),
mergeMap(childId => { childInfo.childId = childId; return logStuff(parentId, childId); ),
map( () => parentId)
);
}
سوال
این بهخوبی کار میکند.
اما اگر من به جای childInfo، childInfo[] داشته باشم، چگونه این کار را انجام دهم؟ چگونه با یک آرایه از childInfo[] برخورد کنم تا در آن مشترک شوم و نتایج درونی observable را بازگردانم؟ من فقط میخواهم تعداد موفقها را برگردانم - یا فقط هر نتیجهای که از هر observable برمیگردد؟ آیا از forkJoin() استفاده کنم؟
من به هیچ وجه نمیتوانم این را بفهمم. کمک میکنی؟
پاسخ شماره ۱
اگر درست منظور سوال شما را متوجه شده باشم، من به این صورت عمل میکنم. توضیحات به صورت داخل متن هستند
function doTheThing(): Observable<any> {
/* ... */
let parentId: number;
// somehow there is an array of children info to be saved
const childInfos = new Array(3).fill({})
return createParent('Parent Info').pipe(
tap(id => (parentId = id)),
concatMap(id => {
// each childInfo is filled with the result of the createParent operation
childInfos.forEach(c => c.parentId = parentId);
// an array of Observable is created - each one represents a createChildRecord operation
const childInfosObs = childInfos.map(ci => createChildRecord(ci))
// all child record operations are executed in parallel via forkJoin
return forkJoin(childInfosObs);
}),
concatMap(childIds => {
// forkJoin returns an array of results with the same order as the array of Observables passed to forkJoin
// therefore we can loop through the array and set each childInfo with its result
childInfos.forEach((ci, i) => ci.childId = childIds[i])
// last thing we do is to log and then return the results of the forkJoin
return logStuff(parentId, childIds).pipe(
map(() => childIds)
);
} )
);
}
توجه داشته باشید که من از concatMap به جای mergeMap استفاده میکنم. مگر اینکه دلیل خاصی وجود داشته باشد، هر زمان که عملیات پایگاه داده یا REST انجام میدهم، ترجیح میدهم از concatMap استفاده کنم فقط برای حفظ ترتیب عملیات.
شما همچنین میتوانید هر Observable موجود در childInfosObs را با منطق کنترل خطا غنیسازی کنید در صورت نیاز، با توجه به اینکه forkJoin خطا میدهد اگر هر یک از Observables که تلاش میکند اجرا کند خطا داشته باشد.
در اینجا یک Stackblitz با نمونهای از راهحل پیشنهادی قرار دارد.
پاسخ شماره ۲
نقل قول بلاک
شما میتوانید ChildInfo[] خود را به یک آرایهٔ قابل مشاهده نگاشت کنید و سپس آن را به این شکل ادغام کنید:
return createParent(parentInfo).pipe(
tap(newParentId => parentId = newParentId),
mergeMap(newParentId => forkJoin(
childInfos.map(childInfo => { // this is an Array map.
childInfo.parentId = parentId;
return createChildRecord(childInfo); // this should return childInfo, since it sounds like a CRUD operation.
})
)),
توجه داشته باشید که این تنها در صورتی کار میکند که createChildRecord تکمیل شود، زیرا در غیر این صورت forkJoin هیچ مقداری صادر نمیکند. من فرض کردم این یک فراخوانی واحد در راهحل من است. اگر createChildRecord نسخه بهروزرسانیشده childInfo شما را برگرداند، زندگی شما در عملیات بعدی نیز باید بسیار آسانتر شود.
createChildRecord(childInfo: ChildInfo): Observable<ChildInfo> {
const childId = /** TBD **/
return of({...childInfo, childId});
}
برای خوانایی، در نظر بگیرید که Observable داخلی را در نوعی متد قرار دهید.
