Home > front end >  equivalent of tap operator that waits for completion
equivalent of tap operator that waits for completion

Time:04-09

I need to open a page on a URL, get the value of a variable, format it and return it.

I have this piece of code, which works :

async function fetchVariable(browser, url) {
    const page = await browser.newPage();
    await page.goto(url);
    const s = await page.evaluate(async () => Promise.resolve(myVariable));
    return s.replace(/\n/g, '');
}

I want to rewrite it without await because... why not. I tried this :

function fetchVariable(browser, url) {
    return from(browser.newPage()).pipe(
            tap(page => page.goto(url)),
            mergeMap((page) => page.evaluate(async () => Promise.resolve(myVariable))),
            map((s) => s.replace(/\n/g, ''))
        ).toPromise();
}

I am stuck on this part : tap(page => page.goto(url)), because tap will not wait before passing to the next operator. And it results in this error message :

UnhandledPromiseRejectionWarning: Error: Execution context was destroyed, most likely because of a navigation.

I basically need to get the page issued by browser.newPage(), wait for the page.gotoUrl(url) to complete and forward the page to the next operator.

It seems that I am lacking a basic operator but I cannot see which one.

CodePudding user response:

Seeing that the goto return a Promise, I'd suggest you to go all in over RxJS and convert everything

function fetchVariable(browser, url) {
  return from(browser.newPage()).pipe(
    switchMap(page =>                               // <-- use `switchMap` here
      from(page.goto(url)).pipe(                    // <-- inner pipe to allow access to `page` variable
        switchMap(page => from(page.evaluate)),
        map(() => myVariable.replace(/\n/g, ''))
      )
    )   
  );
}

About toPromise(): it's being deprecated and will be removed by RxJS 8.

CodePudding user response:

It sounds like you want to do "switchTap". Essentially perform an async side effect and wait for it to complete before continuing.

Instead of using tap, you could do switchMap, but return an observable that returns the original emission:

Here's a simplified example of the original situation:

const myObservable$ = of('source #1').pipe(
  tap(() => myPromiseFn()),
  switchMap(i => of('source #2'))
);

// Order:
//  - source #1
//  - source #2
//  - promise

Solution mentioned above:

const myObservable$ = of('source #1').pipe(
  switchMap(input => from(myPromiseFn()).pipe(
    map(() => input)
  )),
  switchMap(i => of('source #2')),
);

// Order:
//  - source #1
//  - promise
//  - source #2

Here's a little StackBlitz.

  • Related