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

How to handle dynamic array with svelte/store

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

我改编了 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 的代码,但它也不起作用。

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)} 的数组内容,因此数据存在。但为什么它没有出现在我的组件列表中?

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

的调用
set(alldata);

return alldata;

alldata 数组已满之前。 (那是因为 NodeJS 内部是如何工作的——直到调用栈为空时才会调用回调函数)

您可以通过在 alldata 数组中放入 hard-coded 个随机值来测试它。您会看到随机值将出现在浏览器中。

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

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

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>

这应该有效:)

不需要从 init() 返回 alldata,它没有用到
替代 for loop 等待所有数据也可以用 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)
        }