同步填充/修改数组并在承诺中返回修改后的数组

Ste*_*n R 5 javascript synchronous promise reactjs

我对 ReactJS 和 redux 还是很陌生,所以我以前从未真正需要使用它。我正在从我的项目中的 API 调用中检索数据。我想通过向对象添加新属性来修改数据。但是,因为代码不是同步运行的,所以返回的是未修改的数组(我假设)而不是修改后的数组。

export function loadThings() {
  return dispatch => {
    return dispatch({
     type: 'LOAD_THINGS',
     payload: {
       request: {
         url: API_GET_THINGS_ENDPOINT,
         method: 'GET'
       }
     }
    }).then(response => {
      let things = response.payload.data;
      // Retrieve all items for the loaded "things"
      if(things) {
        things.forEach((thing, thingIndex) => {
            things[thingIndex].items = []
            if (thing.hasOwnProperty('channels') && thing.channels) {
              thing.channels.forEach(channel => {
                if (channel.hasOwnProperty('linkedItems') && channel.linkedItems) {
                  channel.linkedItems.forEach(itemName => {
                    dispatch(loadItems(itemName)).then(
                      item => things[thingIndex].items.push(item) // push the items inside the "things"
                    )
                  })
                }
              })
            }
        })
      }
      things.forEach(data => console.log(data.items.length, data.items)) // data.items.length returns 0, data.items returns a populated array
      return things // return the modified array
    }).catch(error => {
      //todo: handle error
      return false
    })
  }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我执行了一个 API 调用,该调用返回名为response. 该数组填充了所有“事物”。如果things存在,我想加载名为“items”的额外信息。根据 things 数组中的信息,我将执行另一个 API 调用(这是通过调度loadItems函数完成的),它返回另一个promise. 根据该 API 调用结果中的数据,我将推入对象的items属性(它是一个数组)things

正如你在评论中看到的,如果我遍历things数组并记录items我刚刚创建的属性,它基本上返回 0 作为长度,这意味着在修改数组things之前返回things数组。

我想知道两件事:

  • 是什么导致我的代码异步运行。它是 dispatch(loadItems(itemName))函数,因为它返回了一个承诺?
  • 我如何能够同步执行我的代码?

请注意:此函数loadThings()还会返回一个 promise(如果您不熟悉 redux)。

您可能有兴趣了解我尝试自己修复代码的内容

由于我无法理解为什么代码异步运行的逻辑,我一直在尝试无望的东西。例如将代码包装在另一个中Promise.all并在该承诺中返回修改后的数组。我使用该then承诺的方法来修改things数组,结果完全相同。可能return things是因为在那个之外被执行promise

我真的很想知道发生了什么

编辑 我已loadItems()根据要求将代码添加到问题中:

export function loadItems(itemName) {
  return dispatch => {
    const url = itemName ? API_GET_ITEMS_ENDPOINT + `/${itemName}` : API_GET_ITEMS_ENDPOINT;
    return dispatch({
      type: 'LOAD_ITEMS',
      payload: {
        request: {
          url: url,
          method: 'GET'
        }
      }
    }).then(response => {
      return response.payload.data
    })
  }
}
Run Code Online (Sandbox Code Playgroud)

And*_*ble 4

我的方法是映射things,为包装在 a 中的所有项目创建承诺数组Promise.all,这给你一个 的数组Promise.all

然后你返回things这个 Promise 数组,Promise.all然后在下一个 then 块中,你可以使用一个简单的 for 循环将数组分配给每个 Promise thing

export function loadThings() {
  return dispatch => {
    return dispatch({
      type: 'LOAD_THINGS',
      payload: {
        request: {
          url: API_GET_THINGS_ENDPOINT,
          method: 'GET'
        }
      }
    }).then(response => {
      let things = response.payload.data;
      // Retrieve all items for the loaded "things"
      const items = things.map((thing) => {
        const thingItems = []
        if (thing.hasOwnProperty('channels') && thing.channels) {
          thing.channels.forEach(channel => {
            if (channel.hasOwnProperty('linkedItems') && channel.linkedItems) {
              channel.linkedItems.forEach(itemName => {
                thingItems.push(dispatch(loadItems(itemName)));
              });
            }
          });
        }

        return Promise.all(thingItems);
      });

      return Promise.all([things, Promise.all(items)])
    })
    .then(([things, thingItems]) => {
      things.forEach((thing, index) => {
        thing.items = thingItems[index];
      })

      return things;
    })
    .catch(error => {
      //todo: handle error
      return false
    })
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:您需要将dispatch(loadItmes(itemName))调用直接推送到thingItems.