Skip to content

Commit

Permalink
feat: make effectScope compatible with async context
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jul 6, 2021
1 parent b845df7 commit 90610e3
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 33 deletions.
20 changes: 16 additions & 4 deletions packages/reactivity/src/effectScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,30 @@ export class EffectScope {
run<T>(fn: () => T): T | undefined {
if (this.active) {
try {
effectScopeStack.push(this)
activeEffectScope = this
this.on()
return fn()
} finally {
effectScopeStack.pop()
activeEffectScope = effectScopeStack[effectScopeStack.length - 1]
this.off()
}
} else if (__DEV__) {
warn(`cannot run an inactive effect scope.`)
}
}

on() {
if (this.active) {
effectScopeStack.push(this)
activeEffectScope = this
}
}

off() {
if (this.active) {
effectScopeStack.pop()
activeEffectScope = effectScopeStack[effectScopeStack.length - 1]
}
}

stop() {
if (this.active) {
this.effects.forEach(e => e.stop())
Expand Down
11 changes: 4 additions & 7 deletions packages/runtime-core/src/apiLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
currentInstance,
isInSSRComponentSetup,
LifecycleHooks,
setCurrentInstance
setCurrentInstance,
unsetCurrentInstance
} from './component'
import { ComponentPublicInstance } from './componentPublicInstance'
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
Expand Down Expand Up @@ -37,12 +38,8 @@ export function injectHook(
// This assumes the hook does not synchronously trigger other hooks, which
// can only be false when the user does something really funky.
setCurrentInstance(target)
const res = target.scope.active
? target.scope.run(() =>
callWithAsyncErrorHandling(hook, target, type, args)
)
: callWithAsyncErrorHandling(hook, target, type, args)
setCurrentInstance(null)
const res = callWithAsyncErrorHandling(hook, target, type, args)
unsetCurrentInstance()
resetTracking()
return res
})
Expand Down
13 changes: 10 additions & 3 deletions packages/runtime-core/src/apiSetupHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
getCurrentInstance,
setCurrentInstance,
SetupContext,
createSetupContext
createSetupContext,
unsetCurrentInstance
} from './component'
import { EmitFn, EmitsOptions } from './componentEmits'
import {
Expand Down Expand Up @@ -248,9 +249,15 @@ export function mergeDefaults(
* @internal
*/
export function withAsyncContext(getAwaitable: () => any) {
const ctx = getCurrentInstance()
const ctx = getCurrentInstance()!
if (__DEV__ && !ctx) {
warn(
`withAsyncContext called without active current instance. ` +
`This is likely a bug.`
)
}
let awaitable = getAwaitable()
setCurrentInstance(null)
unsetCurrentInstance()
if (isPromise(awaitable)) {
awaitable = awaitable.catch(e => {
setCurrentInstance(ctx)
Expand Down
35 changes: 18 additions & 17 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,10 +527,14 @@ export let currentInstance: ComponentInternalInstance | null = null
export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
currentInstance || currentRenderingInstance

export const setCurrentInstance = (
instance: ComponentInternalInstance | null
) => {
export const setCurrentInstance = (instance: ComponentInternalInstance) => {
currentInstance = instance
instance.scope.on()
}

export const unsetCurrentInstance = () => {
currentInstance && currentInstance.scope.off()
currentInstance = null
}

const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component')
Expand Down Expand Up @@ -612,22 +616,19 @@ function setupStatefulComponent(
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)

currentInstance = instance
setCurrentInstance(instance)
pauseTracking()
const setupResult = instance.scope.run(() =>
callWithErrorHandling(setup, instance, ErrorCodes.SETUP_FUNCTION, [
__DEV__ ? shallowReadonly(instance.props) : instance.props,
setupContext
])
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
currentInstance = null
unsetCurrentInstance()

if (isPromise(setupResult)) {
const unsetInstance = () => {
currentInstance = null
}
setupResult.then(unsetInstance, unsetInstance)
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)

if (isSSR) {
// return the promise so server-renderer can wait on it
Expand Down Expand Up @@ -795,11 +796,11 @@ export function finishComponentSetup(

// support for 2.x options
if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
currentInstance = instance
setCurrentInstance(instance)
pauseTracking()
instance.scope.run(() => applyOptions(instance))
applyOptions(instance)
resetTracking()
currentInstance = null
unsetCurrentInstance()
}

// warn missing template/render
Expand Down
5 changes: 3 additions & 2 deletions packages/runtime-core/src/componentProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
ComponentInternalInstance,
ComponentOptions,
ConcreteComponent,
setCurrentInstance
setCurrentInstance,
unsetCurrentInstance
} from './component'
import { isEmitListener } from './componentEmits'
import { InternalObjectKey } from './vnode'
Expand Down Expand Up @@ -411,7 +412,7 @@ function resolvePropValue(
: null,
props
)
setCurrentInstance(null)
unsetCurrentInstance()
}
} else {
value = defaultValue
Expand Down

0 comments on commit 90610e3

Please sign in to comment.