如何使用 svelte/store 处理动态数组

发布于 2025-01-10 07:47:24 字数 1666 浏览 0 评论 0原文

我想从不同的 API 接收多个数据并将其放在一个大数组中。该数组应该可供不同的组件访问。如果调用所有 API 并收集数据,则组件应列出该数组。

我改编了 REPL 中的代码并添加了一个迭代,以调用每个 API。数组将被填充,但组件不显示列表。首先,我认为它与反应性/更新数组有关,但它也不会工作。

import { writable } from 'svelte/store'
export const API_LIST = [
    "http://example1.com/api/",
    "http://example2.com/api/"
];

export function readAll() {
    const { subscribe, update, set } = writable([])
    
    return {
        subscribe,
        init: async () => {
            let alldata = [];
            API_LIST.forEach(async function(apiurl) {
                const res = await fetch(apiurl)
                if (!res.ok) throw new Error('Bad response')
                alldata.push( await res.json() );
                alldata = alldata;
            });
            set(alldata);
            return alldata;
        }
    }
}
export const api_store = readAll()

我的组件:

<script>
    import { api_store } from 'reader.js';
</script>

<div>
    {#await api_store.init()}
        <p>wait for reading...</p>
    {:then}
        <ul>
        {#each $api_store as item}
            <li>
                {item}
            </li>
        {/each}
        </ul>
    {:catch error}
        <p>Something went wrong: {error.message}</p>
    {/await}
</div>

调用所有API并构建数组后,“等待读取”消失,但显示的列表为空。我可以使用 {console.info($api_store)} 查看数组内容,因此数据存在。但为什么它没有出现在我的组件列表中?

I would like to receive several data from different APIs and put it together in one big array. This array should be accessable for different components. The components should list the array, if all APIs are called and the data was collected.

I adapted the code from REPL and added a iteration, to call each API. The array will be filled, but the component is not showing the list. First, I thought its related to reactivity / updating arrays, but it also wont work.

import { writable } from 'svelte/store'
export const API_LIST = [
    "http://example1.com/api/",
    "http://example2.com/api/"
];

export function readAll() {
    const { subscribe, update, set } = writable([])
    
    return {
        subscribe,
        init: async () => {
            let alldata = [];
            API_LIST.forEach(async function(apiurl) {
                const res = await fetch(apiurl)
                if (!res.ok) throw new Error('Bad response')
                alldata.push( await res.json() );
                alldata = alldata;
            });
            set(alldata);
            return alldata;
        }
    }
}
export const api_store = readAll()

My component:

<script>
    import { api_store } from 'reader.js';
</script>

<div>
    {#await api_store.init()}
        <p>wait for reading...</p>
    {:then}
        <ul>
        {#each $api_store as item}
            <li>
                {item}
            </li>
        {/each}
        </ul>
    {:catch error}
        <p>Something went wrong: {error.message}</p>
    {/await}
</div>

After all APIs are called and the array is build up, the "wait for reading" disappears, but the list showed is empty. I can see the array content with {console.info($api_store)}, so the data is present. But why is it not showing up in my component list?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

赠佳期 2025-01-17 07:47:24

当您使用 .foreach() 方法并为其提供异步回调时,
执行了调用

set(alldata);

该代码在

return alldata;

alldata 数组已满之前 。
(这是因为 NodeJS 内部的工作方式 - 在调用堆栈为空之前不会调用回调函数)

您可以通过将硬编码的随机值放入 alldata 数组中来测试它。您将看到随机值将出现在浏览器中。

如果您将 .forach() 调用替换为常规 for 循环
您将阻止代码在对 fetch() 的所有调用完成之前返回。

尝试将您的代码替换为以下内容:

reader.js:

import { writable } from "svelte/store";
export const API_LIST = [
  "http://example1.com/api/",
  "http://example2.com/api/",
];

export function readAll() {
  const { subscribe, update, set } = writable([]);

  return {
    subscribe,
    init: async () => {
      let alldata = [];
      // API_LIST.forEach(async function (apiurl) {
      //   const res = await fetch(apiurl);
      //   if (!res.ok) throw new Error("Bad response");
      //   alldata.push(await res.json());
      //   alldata = alldata;
      // });
      for (let i = 0; i < API_LIST.length; i++) {
        const res = await fetch(API_LIST[i]);
        if (!res.ok) throw new Error("Bad response");
        alldata.push(await res.json());
      }
      set(alldata);
      return alldata;
    },
  };
}
export const api_store = readAll();

Component.svelte:

<script>
  import { api_store } from "./reader.js";
</script>

<div>
  {#await api_store.init()}
    <p>wait for reading...</p>
  {:then}
    <ul>
      {#each $api_store as item}
        <li>
          {item}
        </li>
      {/each}
    </ul>
  {:catch error}
    <p>Something went wrong: {error.message}</p>
  {/await}
</div>

这应该可以工作:)

When you use the .foreach() method and give it an async callback,
the code executed the call to

set(alldata);

and

return alldata;

before alldata array was full.
(That is because how NodeJS internally works - the callback functions will not be called until the call stack is empty)

You can test it by putting hard-coded, random values in the alldata array. You will see the random values will appear inside the browser.

If you will replace the .forach() call to regular for loop
you will prevent the code to return before all calls to fetch() will be finished.

Try to replace your code to the following:

reader.js:

import { writable } from "svelte/store";
export const API_LIST = [
  "http://example1.com/api/",
  "http://example2.com/api/",
];

export function readAll() {
  const { subscribe, update, set } = writable([]);

  return {
    subscribe,
    init: async () => {
      let alldata = [];
      // API_LIST.forEach(async function (apiurl) {
      //   const res = await fetch(apiurl);
      //   if (!res.ok) throw new Error("Bad response");
      //   alldata.push(await res.json());
      //   alldata = alldata;
      // });
      for (let i = 0; i < API_LIST.length; i++) {
        const res = await fetch(API_LIST[i]);
        if (!res.ok) throw new Error("Bad response");
        alldata.push(await res.json());
      }
      set(alldata);
      return alldata;
    },
  };
}
export const api_store = readAll();

Component.svelte:

<script>
  import { api_store } from "./reader.js";
</script>

<div>
  {#await api_store.init()}
    <p>wait for reading...</p>
  {:then}
    <ul>
      {#each $api_store as item}
        <li>
          {item}
        </li>
      {/each}
    </ul>
  {:catch error}
    <p>Something went wrong: {error.message}</p>
  {/await}
</div>

This should work :)

初相遇 2025-01-17 07:47:24

init() 返回 alldata 是不必要的,它没有被使用
除了等待所有数据的 for 循环 之外,还可以使用 Promise.all()
-> REPL

init: async () => {         
            const requests = API_LIST.map(async url => {
                const response = await fetch(url)
                if(response.ok) return await response.json()                        
                else throw new Error('bad response')
            })
            const fetchResults = await Promise.all(requests)
            set(fetchResults)
        }

Returning alldata from init() isn't necessary, it's nowhere used
Alternatively to the for loop waiting for all data could also be handled with Promise.all()
-> REPL

init: async () => {         
            const requests = API_LIST.map(async url => {
                const response = await fetch(url)
                if(response.ok) return await response.json()                        
                else throw new Error('bad response')
            })
            const fetchResults = await Promise.all(requests)
            set(fetchResults)
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文