@@ -29,44 +29,91 @@ export function loadOpenCv(): Promise<Cv> {
2929 return
3030 }
3131 const w = window as unknown as { cv ?: any }
32+ let done = false
3233
33- const settle = ( ) : void => {
34+ const finish = ( ) : boolean => {
3435 const cv = w . cv
35- if ( ! cv ) {
36- reject ( new Error ( 'OpenCV global missing after load' ) )
37- return
36+ if ( done || ! cv || typeof cv . Mat !== 'function' ) {
37+ return false
3838 }
39- if ( typeof cv . then === 'function' ) {
40- // Newer emscripten builds export a module factory promise.
41- cv . then ( ( m : Cv ) => {
42- w . cv = m
43- resolve ( m )
44- } ) . catch ( reject )
39+ done = true
40+ resolve ( cv )
41+ return true
42+ }
43+
44+ // The docs.opencv.org build exposes a global `cv` that is an Emscripten
45+ // Module: its `then` is NOT a real Promise (no `.catch`), and `cv.Mat`
46+ // only appears once the wasm runtime is initialised. We hook every signal
47+ // and, crucially, poll as a safety net so the promise ALWAYS settles
48+ // (otherwise the page hangs forever on "chargement du moteur").
49+ let wired = false
50+ const wire = ( ) : void => {
51+ const cv = w . cv
52+ if ( ! cv || wired ) {
4553 return
4654 }
47- if ( cv . Mat ) {
48- resolve ( cv )
49- return
55+ wired = true
56+ try {
57+ if ( typeof cv . then === 'function' ) {
58+ cv . then ( ( m : Cv ) => {
59+ if ( m && typeof m . Mat === 'function' ) {
60+ w . cv = m
61+ }
62+ finish ( )
63+ } )
64+ }
65+ } catch {
66+ /* Emscripten thenable can throw if called twice — ignored. */
67+ }
68+ cv . onRuntimeInitialized = ( ) : void => {
69+ finish ( )
5070 }
51- cv . onRuntimeInitialized = ( ) : void => resolve ( w . cv )
5271 }
5372
73+ let tries = 0
74+ const poll = window . setInterval ( ( ) => {
75+ tries += 1
76+ wire ( )
77+ if ( finish ( ) ) {
78+ window . clearInterval ( poll )
79+ } else if ( tries >= 300 ) {
80+ // ~30 s
81+ window . clearInterval ( poll )
82+ if ( ! done ) {
83+ done = true
84+ reject ( new Error ( 'OpenCV: délai d’initialisation dépassé' ) )
85+ }
86+ }
87+ } , 100 )
88+
5489 if ( w . cv ) {
55- settle ( )
90+ wire ( )
5691 return
5792 }
5893 const existing = document . getElementById ( 'opencv-js' ) as HTMLScriptElement | null
5994 if ( existing ) {
60- existing . addEventListener ( 'load' , settle )
61- existing . addEventListener ( 'error' , ( ) => reject ( new Error ( 'OpenCV failed to load' ) ) )
95+ existing . addEventListener ( 'load' , wire )
96+ existing . addEventListener ( 'error' , ( ) => {
97+ window . clearInterval ( poll )
98+ if ( ! done ) {
99+ done = true
100+ reject ( new Error ( 'OpenCV failed to load' ) )
101+ }
102+ } )
62103 return
63104 }
64105 const s = document . createElement ( 'script' )
65106 s . id = 'opencv-js'
66107 s . async = true
67108 s . src = OPENCV_URL
68- s . onload = settle
69- s . onerror = ( ) : void => reject ( new Error ( 'OpenCV failed to load' ) )
109+ s . onload = wire
110+ s . onerror = ( ) : void => {
111+ window . clearInterval ( poll )
112+ if ( ! done ) {
113+ done = true
114+ reject ( new Error ( 'OpenCV failed to load' ) )
115+ }
116+ }
70117 document . head . appendChild ( s )
71118 } )
72119 return cvPromise
0 commit comments