Home > front end >  concatAll emits multiple observables and is not subscribing to them
concatAll emits multiple observables and is not subscribing to them

Time:10-17

I'm trying to get a list of courses from one API endpoint and get the assignments associated with each course from another. I am using concatMap to merge the observables but it isn't working as I expected. Instead of emitting the assignments for each course, it's emitting observable objects.

function getCourses() {
  return fromFetch("https://canvas.instructure.com/api/v1/courses", {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization:
        "Bearer 2~WSiTptufbMw24WKz0gWbb34tSabWEbKKVR5bQY3NEL2b5pd7Ah8OEzM8P64FS4ta",
    },
    selector: responseAsJson,
  });
}

function getAssignments(id: number) {
  return fromFetch(
    `https://canvas.instructure.com/api/v1/courses/${id}/assignments?bucket=upcoming`,
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Authorization:
          "Bearer ***",
      },
      selector: responseAsJson,
    }
  );
}

const assignments = getCourses()
  .pipe(
    map((response) => response.map((r: any) => getAssignments(r.id))),
    concatAll(),
)
  
assignments.pipe(tap(r => console.log(r))).subscribe();
/*
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
*/

CodePudding user response:

I was able to make it work by using switchMap and forkJoin

const assignments = getCourses().pipe(
  // unsubscribe from the outer observable after the inner emits
  switchMap((response) =>
    // convert a collection of observables into an observable collection
    forkJoin(response.map((r: any) => getAssignments(r.id)))
  )
);

CodePudding user response:

This is happening because your map() operator returns Promise[] which means it return an array of Promises. concatAll() takes the array as an observable-like input, iterates its values and emits them as separate emissions. In other words, concatAll() will "subscribe" to the array and not Observables inside the array.

So you need to flatten the array into individual emission for example with mergeMap():

const assignments = getCourses()
  .pipe(
    mergeMap((response) => response.map((r: any) => getAssignments(r.id))),
    concatAll(),
)

... or you could also use two cocantAll() but that will look really weird:

const assignments = getCourses()
  .pipe(
    map((response) => response.map((r: any) => getAssignments(r.id))),
    concatAll(),
    concatAll(),
)

The first concatAll will flatten the array and the second one will subscribe to individual Observables.

... or you could just merge all inner Observables

const assignments = getCourses()
  .pipe(
    mergeMap((response) => merge(...response.map((r: any) => getAssignments(r.id)))),
)
  • Related