@@ -238,46 +238,54 @@ function throw_anywidget_error(source) {
238238 throw source ;
239239}
240240
241+ /**
242+ * @typedef InvokeOptions
243+ * @prop {DataView[] } [buffers]
244+ * @prop {AbortSignal } [signal]
245+ */
246+
241247/**
242248 * @template T
243249 * @param {import("@anywidget/types").AnyModel } model
244250 * @param {string } name
245251 * @param {any } [msg]
246- * @param {DataView[] } [buffers]
247- * @param {{ timeout?: number } } [options]
252+ * @param {InvokeOptions } [options]
248253 * @return {Promise<[T, DataView[]]> }
249254 */
250255export function invoke (
251256 model ,
252257 name ,
253258 msg ,
254- buffers = [ ] ,
255- { timeout = 3000 } = { } ,
259+ options = { } ,
256260) {
257261 // crypto.randomUUID() is not available in non-secure contexts (i.e., http://)
258262 // so we use simple (non-secure) polyfill.
259263 let id = uuid . v4 ( ) ;
264+ let signal = options . signal ?? AbortSignal . timeout ( 3000 ) ;
265+
260266 return new Promise ( ( resolve , reject ) => {
261- let timer = setTimeout ( ( ) => {
262- reject ( new Error ( `Promise timed out after ${ timeout } ms` ) ) ;
267+ if ( signal . aborted ) {
268+ reject ( signal . reason ) ;
269+ }
270+ signal . addEventListener ( "abort" , ( ) => {
263271 model . off ( "msg:custom" , handler ) ;
264- } , timeout ) ;
272+ reject ( signal . reason ) ;
273+ } ) ;
265274
266275 /**
267276 * @param {{ id: string, kind: "anywidget-command-response", response: T } } msg
268277 * @param {DataView[] } buffers
269278 */
270279 function handler ( msg , buffers ) {
271280 if ( ! ( msg . id === id ) ) return ;
272- clearTimeout ( timer ) ;
273281 resolve ( [ msg . response , buffers ] ) ;
274282 model . off ( "msg:custom" , handler ) ;
275283 }
276284 model . on ( "msg:custom" , handler ) ;
277285 model . send (
278286 { id, kind : "anywidget-command" , name, msg } ,
279287 undefined ,
280- buffers ,
288+ options . buffers ?? [ ] ,
281289 ) ;
282290 } ) ;
283291}
0 commit comments