Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f936aeb

Browse files
authored
Merge pull request #65 from github/fix-guarantee-order-of-load-events
fix: guarantee order of load events
2 parents 1565634 + 2770e69 commit f936aeb

File tree

2 files changed

+67
-20
lines changed

2 files changed

+67
-20
lines changed

src/index.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ const observer = new IntersectionObserver(entries => {
2121
})
2222

2323

24-
function fire(name: string, target: Element) {
25-
setTimeout(function () {
26-
target.dispatchEvent(new Event(name))
27-
}, 0)
24+
// Functional stand in for the W3 spec "queue a task" paradigm
25+
function task(): Promise<void> {
26+
return new Promise(resolve => setTimeout(resolve, 0))
2827
}
2928

3029
async function handleData(el: IncludeFragmentElement) {
@@ -148,9 +147,12 @@ export default class IncludeFragmentElement extends HTMLElement {
148147

149148
load(): Promise<string> {
150149
observer.unobserve(this)
151-
return Promise.resolve()
150+
// We mimic the same event order as <img>, including the spec
151+
// which states events must be dispatched after "queue a task".
152+
// https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element
153+
return task()
152154
.then(() => {
153-
fire('loadstart', this)
155+
this.dispatchEvent(new Event('loadstart'))
154156
return this.fetch(this.request())
155157
})
156158
.then(response => {
@@ -161,21 +163,27 @@ export default class IncludeFragmentElement extends HTMLElement {
161163
if (!isWildcard(this.accept) && (!ct || !ct.includes(this.accept ? this.accept : 'text/html'))) {
162164
throw new Error(`Failed to load resource: expected ${this.accept || 'text/html'} but was ${ct}`)
163165
}
164-
return response
166+
return response.text()
167+
})
168+
.then(data => {
169+
// Dispatch `load` and `loadend` async to allow
170+
// the `load()` promise to resolve _before_ these
171+
// events are fired.
172+
task().then(() => {
173+
this.dispatchEvent(new Event('load'))
174+
this.dispatchEvent(new Event('loadend'))
175+
})
176+
return data
177+
}, error => {
178+
// Dispatch `error` and `loadend` async to allow
179+
// the `load()` promise to resolve _before_ these
180+
// events are fired.
181+
task().then(() => {
182+
this.dispatchEvent(new Event('error'))
183+
this.dispatchEvent(new Event('loadend'))
184+
})
185+
throw error
165186
})
166-
.then(response => response.text())
167-
.then(
168-
data => {
169-
fire('load', this)
170-
fire('loadend', this)
171-
return data
172-
},
173-
error => {
174-
fire('error', this)
175-
fire('loadend', this)
176-
throw error
177-
}
178-
)
179187
}
180188

181189
fetch(request: RequestInfo): Promise<Response> {

test/test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,45 @@ suite('include-fragment-element', function() {
426426
})
427427
})
428428

429+
suite('event order', () => {
430+
const originalSetTimeout = window.setTimeout
431+
setup(() => {
432+
// Emulate some kind of timer clamping
433+
let i = 60
434+
window.setTimeout = (fn, ms, ...rest) => originalSetTimeout.call(window, fn, ms + (i -= 20), ...rest)
435+
})
436+
teardown(() => {
437+
window.setTimeout = originalSetTimeout
438+
})
439+
440+
test('loading events fire in guaranteed order', function() {
441+
const elem = document.createElement('include-fragment')
442+
const order = []
443+
const connected = []
444+
const events = [
445+
when(elem, 'loadend').then(() => {
446+
order.push('loadend')
447+
connected.push(elem.isConnected)
448+
}),
449+
when(elem, 'load').then(() => {
450+
order.push('load')
451+
connected.push(elem.isConnected)
452+
}),
453+
when(elem, 'loadstart').then(() => {
454+
order.push('loadstart')
455+
connected.push(elem.isConnected)
456+
})
457+
]
458+
elem.src = '/hello'
459+
document.body.appendChild(elem)
460+
461+
return Promise.all(events).then(() => {
462+
assert.deepStrictEqual(order, ['loadstart', 'load', 'loadend'])
463+
assert.deepStrictEqual(connected, [true, false, false])
464+
})
465+
})
466+
})
467+
429468
test('sets loading to "eager" by default', function() {
430469
const div = document.createElement('div')
431470
div.innerHTML = '<include-fragment loading="lazy" src="/hello">loading</include-fragment>'

0 commit comments

Comments
 (0)