Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning empty promise

I'm implementing this button in my app.

In my button component I have:

  @Input() callback: () => Promise<null>;
  onClick() {
    this.spinnerService.show();
    this.callback().then(() => {
      this.spinnerService.hide();
    }, () => {
      this.spinnerService.hide();
    });
  }

(I don't use async / await because the "tech lead" doesn't want me too)

The component template:

<my-button [callback]="doSomething">
</my-button>

Everything works fine when I pass this kind of code to the component's input:

doSomething = () => {
  return myService.doSomething()
    .toPromise()
    .then((response) => console.log('promise resolved'));
}

But I need to break from this function earlier:

doSomething() {
  if (condition) {
    return;
  }
  return myService.doSomething()
    .toPromise()
    .then((response) => console.log('promise resolved'));
}

I get

ERROR TypeError: Cannot read property 'then' of undefined

I've tried forcing a promise with the same result

  if (condition) {
    return Promise.resolve(null);
    // or
    return new Promise((resolve) => { resolve(null); });
  }
like image 467
gyc Avatar asked Mar 15 '18 14:03

gyc


People also ask

What does empty Promise mean?

Empty-promise definition (idiomatic) A promise that is either not going to be carried out, worthless or meaningless.

What does returning a Promise do?

Returns a new Promise object that is resolved with the given value. If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise, the returned promise will be fulfilled with the value.

How do I resolve a returned Promise?

The Promise.resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise.resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.


1 Answers

For this input

@Input() callback: () => Promise<null>;

there is no guarantee that it's assigned to proper value. Additional check if callback was specified should be added.

A safer way to manage this is to use async functions in these places because they consistently use promises:

  async onClick() {
    this.spinnerService.show();

    try {
      if (this.callback)
        await this.callback()
    } finally {
      this.spinnerService.hide();
    }
  }

doSomething is supposed to return a promise unconditionally, and this can also be addressed with async:

async doSomething() {
  if (!condition)
    return myService.doSomething().toPromise();
}

If async functions can't be used for some reason (although there are no good ones, because they are spec-compliant, first-class citizens in TypeScript), promises should be consistently processed in functions (also helpful for testing). doSomething isn't properly typed, and this allows improper function return. It should be:

  onClick(): Promise<void> {
    this.spinnerService.show();

    return Promise.resolve(this.callback ? this.callback() : undefined)
    .catch(() => {})
    .then(() => {
      this.spinnerService.hide();
    });
  }

and

doSomething(): Promise<void> {
  if (condition) {
    return Promise.resolve();
  }

  return myService.doSomething().toPromise();
}

And the type of callback will be Promise<void>, not Promise<null>.

Regular (non-arrow) doSomething method should be bound to this in constructor.

like image 107
Estus Flask Avatar answered Sep 20 '22 15:09

Estus Flask