Browse Source

fix(core): cleanup timeouts for async components (#9649)

close #9648
dev
Alexander Schwartz 6 years ago
committed by Evan You
parent
commit
02d21c265c
  1. 16
      src/core/vdom/helpers/resolve-async-component.js
  2. 64
      test/unit/features/component/component-async.spec.js

16
src/core/vdom/helpers/resolve-async-component.js

@ -65,6 +65,8 @@ export function resolveAsyncComponent (
if (owner && !isDef(factory.owners)) {
const owners = factory.owners = [owner]
let sync = true
let timerLoading = null
let timerTimeout = null
;(owner: any).$on('hook:destroyed', () => remove(owners, owner))
@ -75,6 +77,14 @@ export function resolveAsyncComponent (
if (renderCompleted) {
owners.length = 0
if (timerLoading !== null) {
clearTimeout(timerLoading)
timerLoading = null
}
if (timerTimeout !== null) {
clearTimeout(timerTimeout)
timerTimeout = null
}
}
}
@ -121,7 +131,8 @@ export function resolveAsyncComponent (
if (res.delay === 0) {
factory.loading = true
} else {
setTimeout(() => {
timerLoading = setTimeout(() => {
timerLoading = null
if (isUndef(factory.resolved) && isUndef(factory.error)) {
factory.loading = true
forceRender(false)
@ -131,7 +142,8 @@ export function resolveAsyncComponent (
}
if (isDef(res.timeout)) {
setTimeout(() => {
timerTimeout = setTimeout(() => {
timerTimeout = null
if (isUndef(factory.resolved)) {
reject(
process.env.NODE_ENV !== 'production'

64
test/unit/features/component/component-async.spec.js

@ -2,6 +2,42 @@ import Vue from 'vue'
import { Promise } from 'es6-promise'
describe('Component async', () => {
const oldSetTimeout = window.setTimeout;
const oldClearTimeout = window.clearTimeout;
// will contain pending timeouts set during the test iteration
// will contain the id of the timeout as the key, and the the millisecond timeout as the value
// this helps to identify the timeout that is still pending
let timeoutsPending = {};
beforeEach(function () {
// reset the timeouts for this iteration
timeoutsPending = {};
window.setTimeout = function(func, delay) {
let id = oldSetTimeout(function() {
delete timeoutsPending[id];
func();
}, delay);
timeoutsPending[id] = delay;
return id
};
window.clearTimeout = function(id) {
oldClearTimeout(id);
delete timeoutsPending[id];
};
})
afterEach(function () {
window.setTimeout = oldSetTimeout;
window.clearTimeout = oldClearTimeout;
// after the test is complete no timeouts that have been set up during the test should still be active
// compare stringified JSON for better error message containing ID and millisecond timeout
expect(JSON.stringify(timeoutsPending)).toEqual(JSON.stringify({}))
})
it('normal', done => {
const vm = new Vue({
template: '<div><test></test></div>',
@ -343,6 +379,34 @@ describe('Component async', () => {
}, 50)
})
it('should not have running timeout/loading if resolved', done => {
const vm = new Vue({
template: `<div><test/></div>`,
components: {
test: () => ({
component: new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ template: '<div>hi</div>' })
Promise.resolve().then(() => {
Vue.nextTick(next)
})
}, 10)
}),
loading: { template: `<div>loading</div>` },
delay: 30,
error: { template: `<div>error</div>` },
timeout: 40
})
}
}).$mount()
function next () {
expect(vm.$el.textContent).toBe('hi')
// the afterEach() will ensure that the timeouts for delay and timeout have been cleared
done()
}
})
// #7107
it(`should work when resolving sync in sibling component's mounted hook`, done => {
let resolveTwo

Loading…
Cancel
Save