Throw new Error in async method

Hi, is there a way to use throw new Error('my custom error'); inside an async method and still have it return the red alert with the custom error message?

Thanks in advance,

Simone

You have to show some context.

1 Like

Hi robert,
i’m registering the capability ‘speaker_playng’:

this.registerCapabilityListener(‘speaker_playing’, async (value) => { … }

I would like to insert a timer that throws an error if playback does not start after 5 seconds of pressing the play button:

this.registerCapabilityListener(‘speaker_playing’, async (value) => {
this.playbackTimer = this.homey.setTimeout(() => {
throw new Error(‘Playback Error!!!’);
}, 5000);
}

Unfortunately trying to throw a custom error from the (async) setTimeout method does not show the homey red alert with the error message.

This won’t work because using a timeout creates a separate context from the capability listener. The error is thrown inside the “timer context”, not the “capability listener context”.

I’m not sure how you determine if playback has started, but say that you use an (async) method isPlaying() for that.

In that case, you can use something like this (untested):

const delay   = ms => new Promise(resolve => this.homey.setTimeout(resolve, ms));
const timeout = (promise, ms, err) => Promise.race([ promise, delay(ms).then(() => throw Error(err)) ]);

this.registerCapabilityListener('speaker_playing', async (value) => {
  await timeout(this.isPlaying(), 5000, 'Playback Error!!!');
}

This is the old “style” timeout, there is now an “official” one.

Not sure why you’re removing the line above it that uses that exact method :stuck_out_tongue_winking_eye:

It’s still early in the morning, but in my opinion it should read like this :thinking:

this.registerCapabilityListener('speaker_playing', async (value) => {
  this.homey.setTimeout(this.isPlaying(), 5000, 'Playback Error!!!');
}

Yes, it is. Read back on what the issue is that we’re trying to solve :stuck_out_tongue:

This is new, but isn’t the problem more with the Homey app?

BDW: I had to change all timeouts some time ago because the app no longer closed properly.

No.

My proposed solution is using the SDK timeout implementation:

const delay   = ms => new Promise(resolve => this.homey.setTimeout(resolve, ms));
                                             ^^^^^^^^^^^^^^^^^^^^^
const timeout = (promise, ms, err) => Promise.race([ promise, delay(ms).then(() => throw Error(err)) ]);
1 Like

Ok my mistake !!!

And what do we learn from this, first drink coffee and then read & write in the forum, then you don’t embarrass yourself…

Very big SORRY :man_facepalming:…

1 Like

Thanks to both of you.

@robertkep I tried to use your code but without success. I’ll give you some more information on the structure of my class.

I’m working on device.js

in onOnit I define the variable for the timer:

this.playbackTimer = null;

in registerCapabilityListener(‘speaker_playing’) I set the timer as already indicated:

this.registerCapabilityListener(‘speaker_playing’, async (value) => {
this.playbackTimer = this.homey.setTimeout(() => {
…
throw new Error(‘Playback Error!!!’);
}, 5000);
}

in an async event that fires when the playback starts I clear the timer:

this.on(‘playerChanged’, async (playerDetail) => {
…
this.homey.clearTimeout(this.playbackTimer);
}

In this way if the async event is not called or is called after 5 seconds from pressing the play button the timer callback fires, resets the player (and up to this point everything works) but does not return any error alert.

I think you can use something like this:

// a new method that waits for an event until a timeout is reached
waitForEvent(event, timeout) {
  return new Promise((resolve, reject) => {
    this.once(event, resolve);
    this.homey.setTimeout(reject, timeout);
  });
}

// to use:
this.registerCapabilityListener('speaker_playing', async (value) => {
  try {
    const eventData = await this.waitForEvent('playerChanged', 5000);
  } catch(e) {
    throw Error('Playback Error!!!');
  }
});

Thanks Robert! Works like a charm!