@@ -222,46 +222,48 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
222222 const startTime = performance . now ( ) ;
223223 let messaging : WidgetMessaging | undefined = messagingStore . getMessagingForUid ( this . widgetUid ) ;
224224 // The widget might still be initializing, so wait for it in an async
225- // event loop. We need the messaging to be both present and started, so
226- // we register listeners for both cases. Note that due to React strict
227- // mode, the messaging could even be aborted and replaced by an entirely
228- // new messaging while we are waiting here!
229- while ( ! messaging ) {
230- logger . info ( `No messaging for ${ this . widgetUid } ` ) ;
231- await new Promise < void > ( ( resolve , reject ) => {
232- // We don't have `messaging` either so wait for the
233- const onStoreMessaging = ( uid : string , m : WidgetMessaging ) : void => {
234- if ( uid === this . widgetUid ) {
235- messagingStore . off ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
236- messaging = m ;
237- resolve ( ) ;
238- }
239- } ;
240- messagingStore . on ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
241-
242- setTimeout (
243- ( ) => {
244- messagingStore . off ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
245- reject ( new Error ( `Messaging for call in ${ this . roomId } not present; timed out` ) ) ;
246- } ,
247- startTime + TIMEOUT_MS - performance . now ( ) ,
248- ) ;
249- } ) ;
250- }
225+ // event loop. We need the messaging to be both present and started
226+ // (have a connected widget API), so register listeners for both cases.
227+ while ( ! messaging ?. widgetApi ) {
228+ if ( messaging ) logger . debug ( `Messaging present but not yet started for ${ this . widgetUid } ` ) ;
229+ else logger . debug ( `No messaging yet for ${ this . widgetUid } ` ) ;
230+ const recheck = Promise . withResolvers < void > ( ) ;
231+ const currentMessaging = messaging ;
232+
233+ // Maybe the messaging is present but not yet started. In this case,
234+ // check again for a widget API as soon as it starts.
235+ const onStart = ( ) : void => recheck . resolve ( ) ;
236+ currentMessaging ?. on ( WidgetMessagingEvent . Start , onStart ) ;
237+
238+ // Maybe the messaging is not present at all. It's also entirely
239+ // possible (as shown in React strict mode) that the messaging could
240+ // be abandoned and replaced by an entirely new messaging object
241+ // while we were waiting for the original one to start. We need to
242+ // react to store updates in either case.
243+ const onStoreMessaging = ( uid : string , m : WidgetMessaging ) : void => {
244+ if ( uid === this . widgetUid ) {
245+ messagingStore . off ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
246+ messaging = m ; // Check the new messaging object on the next iteration of the loop
247+ recheck . resolve ( ) ;
248+ }
249+ } ;
250+ messagingStore . on ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
251251
252- while ( ! messaging . widgetApi ) {
253- const foundMessaging = messaging ;
254- logger . info ( `No widgetApi for ${ this . widgetUid } ` ) ;
255- await new Promise < void > ( ( resolve , reject ) => {
256- // We have messasing but are waiting for the widgetApi
257- foundMessaging . once ( WidgetMessagingEvent . Start , ( ) : void => resolve ( ) ) ;
252+ // Race both of the above recheck signals against a timeout.
253+ const timeout = setTimeout (
254+ ( ) => recheck . reject ( new Error ( `Widget for call in ${ this . roomId } not started; timed out` ) ) ,
255+ TIMEOUT_MS - ( performance . now ( ) - startTime ) ,
256+ ) ;
258257
259- setTimeout (
260- ( ) => reject ( new Error ( `Messaging for call in ${ this . roomId } did not start; timed out` ) ) ,
261- startTime + TIMEOUT_MS - performance . now ( ) ,
262- ) ;
263- } ) ;
258+ try {
259+ await recheck . promise ;
260+ } finally {
261+ currentMessaging ?. off ( WidgetMessagingEvent . Start , onStart ) ;
262+ messagingStore . off ( WidgetMessagingStoreEvent . StoreMessaging , onStoreMessaging ) ;
263+ clearTimeout ( timeout ) ;
264+ }
264265 }
266+
265267 logger . debug ( `Widget ${ this . widgetUid } now ready` ) ;
266268 return ( this . widgetApi = messaging . widgetApi ) ;
267269 }
0 commit comments