Nate
Nate

Reputation: 7856

Pass arguments into array of observables

I'm using a forkJoin of observables but I'm having troubles to pass arguments in my observables. Lines of code worth a thousand words:

for(var key in pdfObjects){
    let pdf = {pdfObjects[key]};
    observables.push(new Observable(observer => {
        this.createPDF(pdf).subscribe((pdfFile) => {
            // Do something with my pdfFile
            observer.complete();
        })
    })
}
Observable.forkJoin(observables).subscribe(
    (next) => {},
    (error) => {},
    (completed) => {
        console.log('completed');
    }
);

(I have simplified the code for better clarity)

As you can see here, the problem is that when the code executes the observables, the pdf variable is equal to the last pdfObjects instead of being a different variable for each observable.

The question is how can I 'pass' and 'copy' my pdf variable so it's a diffent one for each observable?

Upvotes: 0

Views: 868

Answers (2)

Jason Goemaat
Jason Goemaat

Reputation: 29194

@Thierry is right, here's an example to show what's going on (fiddle)

var pdfObjects = {
  a: 'Object A',
  b: 'Object B',
  c: 'Object C'
};

for(let key in pdfObjects) {
  let pdf = pdfObjects[key];
  function createObservable(p) {
    console.log('createObservable:', p, pdf, key);
    return Rx.Observable.create(observer => {
      console.log('createObservable.create:', p, pdf, key);
      observer.onNext({ p: p, pdf: pdf, key: key});
      observer.onCompleted();
    });
  }
  observables.push(createObservable(pdf));
}

Result:

createObservable: Object A Object A a
createObservable: Object B Object B b
createObservable: Object C Object C c
createObservable.create: Object A Object C c
createObservable.create: Object B Object C c
createObservable.create: Object C Object C c

When createObservable is called, each value is as you would expect. But when you subscribe to your observables, the anonymous function that takes observer is called by RxJs and is using the current values for pdf and key which are what they were the last time through the loop. But because you're creating a function, there's a new scope and the argument 'p' in that scope is the value the function was called with.

Upvotes: 0

Thierry Templier
Thierry Templier

Reputation: 202138

You should call the next method instead of the complete one:

observables.push(new Observable(observer => {
  this.createPDF(pdf).subscribe((pdfFile) => {
    // Do something with my pdfFile
    observer.next(pdf); // <-----
  });
})

Edit

Your problem is related to the use of closures within loops.

You could break with a method:

createObservable(pdf:any) {
  return new Observable(observer => {
    this.createPDF(pdf).subscribe((pdfFile) => {
      // Do something with my pdfFile
      observer.complete();
    });
  });
}

otherMethod() {
  for(var key in pdfObjects){
    let pdf = {pdfObjects[key]};
    observables.push(this.createObservable(pdf));
  }
  (...)
}

See this question for more details:

See this plunkr: https://plnkr.co/edit/P4BfwnA1HEw7KU4i3RbN?p=preview.

Upvotes: 4

Related Questions