Subscribe on changes!

Suspense issues withAsyncContext called without active current instance

avatar
Dec 22nd 2022

Vue version

3.2.45

Link to minimal reproduction

https://sfc.vuejs.org/#__SSR__eNp1VEtv00AQ/isjc0gKydqtWlKMWwkhcSqlgnLCHNb2ON3U3jW760RRmv/OrF95tVEkz2vn+c1svC9VxZY1eqEXmVSLyoJBW1e3sRRlpbSFr6qsINeqhBHzHePMR7GMZaqksVAVfI3awA38iSXABkQWwvkEpEif73mJIcTePdGS6NiD7WRnFUwgF9rY3uxbz8TeBAq+U9x1tJO38e7rMkFNgWawjeXfWEZ+mz4lTozFkuwsEgcQWZ4UCCl5NDexJ6QUcv7oZLHXGDiTRGXrjgHov9Gv2lQoTevnUOcedVHgXYY5rwsXvLeysY2a1i2nudIUts0bhOw7Rs0In3E9qJjInKhlBimJ/L3okb9X2qup5LwoEp4+7z1yeg0Zt3zaOhUZ+Q+G6gcrUc7B6JSUT9ZWJvT91WrF5pov6a1mqSr9lvQvgiu8/Bgkl7NPeIFXyeU1xzSYzc6DNLjOrsj3Qdrud6d4Ro3vy2eMHUU3FZe3QUCTdMRB+r7VbzXheC6RfzK0bsb+bshEu/kTvefLm3gt5qclr9jCKElbsXHmBJpGQTMLoZE4Ge2B43fNMnnqlmNhmNJznyima2lFiQxNOU20Whka84K8NEvQ+fBJuEQ91Sgz1G7ib/s8Mj3x69zSPmyplH5V3WYftcuh4RR6+w222WvQCFtstG8eRGprjb9/3r0O5OPxbzbd5Jm7DO4awMtLLxrOAHyg2t3/Q6/q7wBsqbBTvOzc7h8GMj6BEU26r6rD09Ey9Qew4HJOxbhpD8ewu3VaVe7S0boLiQ+OGzdwGLXBR4QOsOuKjtaPZIGpnYDGf7XQSNfO6pqKoOGcfW4wdXA/d90k9+MzuLmF0aiz4ysuLEhcAUUshcFxa0C5PRK4VG07wXdun1heKKXHDam5zFRJuvdwEQTBGTW1+fYJDDfT2/4HxsTlNA==

Steps to reproduce

Wrap a Suspense tag around a component with a top-level await.

What is expected?

The Suspense will render the #fallback element, and render the #default element once its Promise is resolved.

What is actually happening?

Neither the parent nor the child component are rendered. I receive an error

withAsyncContext called without active current instance. This is likely a bug.

System Info

System:
    OS: Windows 10 10.0.22621
    CPU: (8) x64 Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
    Memory: 5.37 GB / 15.71 GB
Binaries:
    Node: 18.7.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 3.3.0 - C:\Program Files\nodejs\yarn.CMD
    npm: 8.19.1 - C:\Program Files\nodejs\npm.CMD
Browsers:
    Edge: Spartan (44.22621.819.0), Chromium (108.0.1462.54)
    Internet Explorer: 11.0.22621.1
npmPackages:
    vue: ^3.2.45 => 3.2.45

Any additional comments?

{
    "vue": "^3.2.45",
    "typescript": "^4.9.3",
    "vite": "^4.0.0",
    "vue-router": "^4.1.6",
    "vue-tsc": "^1.0.11"
}
avatar
Dec 22nd 2022

Just to make sure: Are you seeing this exact problem in an App? or just in the playground? Is your app using SSR? Is it Nuxt?

Because in the playground, the error goes away when you disable "SSR" in the upper right.

avatar
Dec 22nd 2022

I am seeing the issue in #7393 in my app. The app is using SSR and not using Nuxt. When I replicate the problem area in the playground, I am getting a similar but different issue as described in this bug. Strangely enough, in the playground example, nothing even renders for me (SSR on or off) unless:

  1. I remove the await from the Comp.vue. Content renders as expected.
  2. I remove the Suspense from App.vue and just leave the placeholder tr and contents. This will then render what was in the now-deleted Suspense #default block with the correct content from the v-for.
avatar
Dec 22nd 2022

In your playground, you don't resolve the promise in the async components, so they never render.

avatar
Dec 22nd 2022

My mistake, I missed the resolve in the playground. I have updated that which can be found in this playground.

After the resolution fix, when SSR is off this renders correctly without errors. However, with SSR enabled I receive the following errors and nothing is rendered. Why might that be the case when using this with Suspense?

image

avatar
Dec 22nd 2022

It's likely just a bug in the playground, which had to mind of stimulate the Server side step, as it all runs in the client.

avatar
Dec 23rd 2022

I have found the cause of the problem. The vue.runtime.esm-browser.js and server-renderer.esm-browser.js modules contain some of the same code, as shown below:

let currentInstance = null;
const setCurrentInstance = (instance) => {
    currentInstance = instance;
    instance.scope.on();
};
const unsetCurrentInstance = () => {
    currentInstance && currentInstance.scope.off();
    currentInstance = null;
};

Because renderToString is run in the server-renderer.esm-browser.js module and updates the value of currentInstance when it runs, it is not possible to access this value in the vue.runtime.esm-browser.js module because they belong to different module scopes. Therefore, when the getCurrentInstance function is run in the vue.runtime.esm-browser.js module, it cannot get the correct value, which leads to this bug.

avatar
Feb 10th 2023

I have found the cause of the problem. The vue.runtime.esm-browser.js and server-renderer.esm-browser.js modules contain some of the same code, as shown below:

let currentInstance = null;
const setCurrentInstance = (instance) => {
    currentInstance = instance;
    instance.scope.on();
};
const unsetCurrentInstance = () => {
    currentInstance && currentInstance.scope.off();
    currentInstance = null;
};

Because renderToString is run in the server-renderer.esm-browser.js module and updates the value of currentInstance when it runs, it is not possible to access this value in the vue.runtime.esm-browser.js module because they belong to different module scopes. Therefore, when the getCurrentInstance function is run in the vue.runtime.esm-browser.js module, it cannot get the correct value, which leads to this bug.

#7672 Is this the same cause causing the problem?

avatar
Feb 10th 2023

@baiwusanyu-c I believe the root cause is the same.