问题
I have following scenario - 4 callback functions A,B,C,D which are called form old-style like library (which use some API requests inside so execution time is unknown/random but proper order of results (by finished task time) is important for me) - and I want to synchronise data which they return to one obserwable result string using rxjs.
function getData() {
// --- BELOW Part can be EDIT ---
let obs = new ReplaySubject(1); // this is example you can use an type
function A(n) {
let r= 'A'.repeat(n);
}
function B(n) {
let r= 'B'.repeat(n);
}
function C(n) {
let r= 'C'.repeat(n);
}
function D(n) {
let r= 'D'.repeat(n);
obs.next(r);
}
// --- BELOW Part can NOT be edit ---
runLib(A,B,C,D)
return obs
}
In below snippet value of finalResult
is DDDDD
which is wrong. Proper value of finalResult
string should be AADDDDDCCCCBBB
.
// SET-UP - NOT EDIT Below code
const { of, Observable, ReplaySubject } = rxjs;
const { map, switchMap, delay } = rxjs.operators; // example
// simulated lib functions
function libA(callback) { setTimeout( _=>callback(2), 1000); }
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }
function runLib(cA,cB,cC,cD) {
libA( cA ); libB( cB ); libC( cC ); libD( cD );
}
getData().subscribe(finalResult => {
console.log(finalResult) // The result is WRONG here!
}, e=>{}, _=> console.log('finished - unsubscribed'));
function getData() {
// --- BELOW Part can be EDIT ---
let obs = new ReplaySubject(1); // this is example, you can use diffrend observale kind
function A(n) {
let r= 'A'.repeat(n);
}
function B(n) {
let r= 'B'.repeat(n);
}
function C(n) {
let r= 'C'.repeat(n);
}
function D(n) {
let r= 'D'.repeat(n);
obs.next(r);
}
// --- BELOW Part can NOT be edit ---
runLib(A,B,C,D)
return obs
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>
In snippet I mark code inside getData()
which can be edit in solution (may be it looks little awkward but this is exactly what I need) (you can also find there finalResult
but not edit that part of code). It is possible? How to do it?
回答1:
The best thing to do in such case is to wrap the library functions to return Observable
and then use forkJoin
to wait for all the results.
I took your code and modified it to get the desired result, you would need to:
- in each callback emit the result to a subject.
- return an
Observable
which wait forn
emission - in this case 4 - map the emissions into a single string
The final getData
function will look like this:
function getData() {
// --- BELOW Part can be EDIT ---
const result$: Subject<string> = new Subject<string>();
const obs = result$.asObservable().pipe(
bufferCount(4), // or any desired number of callback
map((results: string[]) => results.join(''))
);
function A(n) {
let r = "A".repeat(n);
result$.next(r);
}
function B(n) {
let r = "B".repeat(n);
result$.next(r);
}
function C(n) {
let r = "C".repeat(n);
result$.next(r);
}
function D(n) {
let r = "D".repeat(n);
result$.next(r);
}
// --- BELOW Part can NOT be edit ---
runLib(A, B, C, D);
return obs;
}
You can find the full code in this stackblitz or run below snippet
// SET-UP - NOT EDIT Below code
const { Subject } = rxjs;
const { take, bufferCount, map } = rxjs.operators; // example
// simulated lib functions
function libA(callback) { setTimeout( _=>callback(2), 1000); }
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }
function runLib(cA,cB,cC,cD) {
libA( cA ); libB( cB ); libC( cC ); libD( cD );
}
getData().subscribe(finalResult => {
console.log(finalResult) // The result is WRONG here!
}, e=>{},_=> console.log('finished - unsubscribed'));
function getData() {
// --- BELOW Part can be EDIT ---
const result$ = new Subject();
function A(n) {
let r = "A".repeat(n);
result$.next(r);
}
function B(n) {
let r = "B".repeat(n);
result$.next(r);
}
function C(n) {
let r = "C".repeat(n);
result$.next(r);
}
function D(n) {
let r = "D".repeat(n);
result$.next(r);
}
const obs = result$.pipe(
bufferCount(4), // or any desired number of callback
take(1),
map(results=> results.join``)
);
// --- BELOW Part can NOT be edit ---
runLib(A, B, C, D);
return obs;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>
回答2:
How about the following: (I like @Tal Ohana's
answer, but the subject in his solution will never bet unsubscribed which may result in a memory lick)
function getData() {
let obs = new Subject<string>();
function A(n: number) {
let r = 'A'.repeat(n);
obs.next(r);
}
function B(n: number) {
let r = 'B'.repeat(n);
obs.next(r);
}
function C(n: number) {
let r = 'C'.repeat(n);
obs.next(r);
}
function D(n: number) {
let r = 'D'.repeat(n);
obs.next(r);
}
runLib(A, B, C, D)
return obs.pipe(
scan((acc, value) => acc + value),
take(4),
last()
)
}
来源:https://stackoverflow.com/questions/63072094/old-style-callbacks-synchronisation-using-observables