同步 populate/modify 一个数组和 return promise 中修改后的数组

Synchronously populate/modify an array and return the modified array inside a promise

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

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
    })
  }
}

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

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

我想知道两件事:

请注意:此函数 loadThings() 也是一个承诺(如果您不熟悉 redux)。

您可能有兴趣了解我尝试修复代码的方法

由于我无法理解为什么代码是 运行 异步的逻辑,所以我一直在尝试无望的事情。例如将代码包装在另一个 Promise.all 和 return 该承诺中修改后的数组中。我使用该承诺的 then 方法修改 things 数组,结果完全相同。可能是因为 return thingspromise.

之外执行

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

编辑 我已经按照要求在问题中添加了 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
    })
  }
}

我想你可以像下面这样重构它:

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 ) {
        return Promise.all( things.reduce( (promises, thing) => {
          if (thing.channels) {
            thing.items = [];
            promises.push( ...thing.channels.map( channel => 
              channel.linkedItems && 
              channel.linkedItems.map( item => 
                loadItems(item).then( result => thing.items.push( result ) ) 
              ) ).flat().filter( i => !!i ) );
          }
          return promises;
        }, []) );
      }
      return things;
    }).catch(error => {
      //todo: handle error
      return false
    })
  }
}

如果您有 things,它会检查通道和该通道的 linkedItems,并创建一个将结果推送回 thing.items 的承诺数组。

通过返回 Promise.allloadThings 的继续只会在 Promise.all 被解析时完成。如果没有东西,只会返回东西(这将是一个虚假值,所以我想知道该声明的有效性)

我还没有实际测试重构,所以可能有一些括号需要根据您的情况进行调整,但我想它给了您一个想法?

我的方法是在 things 上映射,为包含在 Promise.all 中的所有项目创建承诺数组,这会为您提供 Promise.all 的数组。

然后你 return things 和这个承诺数组在另一个 Promise.all 和下一个 then 块中,你可以将数组分配给每个 thing一个简单的 for 循环:

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
    })
  }
}

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