【问题标题】:How to call a function after map() loop executedmap() 循环执行后如何调用函数
【发布时间】:2020-02-13 22:42:39
【问题描述】:

我正在使用 react-native firebase 将我的媒体保存到 firebase。我有一组需要保存在 Firebase 中的 URL。为此,我使用 map() 来一一保存。保存后,我将成功 URL 推送到数组。 我需要用这个数组作为参数调用一个函数。所以我需要在数组完成后调用这个函数。 我搜索了很多,但我没有找到这个任务的一个很好的解释。谁能帮我。谢谢。

var mediaArray = []; //array of success callbacks values
var completedMediaSurveysAnswers = [{}, {}, {}]; //object array of URLs and media types
completedMediaSurveysAnswers.map((i) => {
  try {
    const storage = firebase.storage();
    const mRef = storage.ref('portal').child('Survey/Image/user/' + uuidv4() + 'media');
    mRef.putFile(i.urlPath, {
        contentType: i.mediaType
      })
      .on('state_changed', snapshot => {},
        err => {
          console.log('Failed to upload file to firebase storage')
        },
        uploadedFile => {
          // Success
          this.setState({
            mediaPath: uploadedFile.downloadURL
          })
          mediaArray.push(this.state.mediaPath)
        });
  } catch (error) {
    console.log(error)
  }
})

//need to call this function after loop is done
saveAnswers(mediaArray)

【问题讨论】:

  • 不要使用.map进行简单的迭代。为此使用.forEach。映射是将每个条目转换为另一个条目的过程,因此您将获得一个数组的 1:1 副本,其中所有项都由映射操作转换。
  • 如上...除非执行 .map 的目的是返回一个 Promise 数组,您可以等待
  • 虽然@VLAZ 的评论是正确的,但如果使用得当,.map() 将是一个有效的选项。因此,与其手动将元素推入mediaArray,不如将​​其推入mediaArray = completedMediaSurveysAnswers.map(...)(加上回调中的一些调整)。但这在这种情况下无济于事-> How do I return the response from an asynchronous call?
  • @VLAZ 谢谢。我将使用 forEach。但我对在这种情况下如何使用等待/异步或承诺感到困惑:(
  • 好吧,第一步,你需要一些 Promise 来等待……你的代码中有没有?不,你没有 - 所以,你需要做一些:D

标签: javascript arrays loops react-native react-native-firebase


【解决方案1】:

使用 .map 很好,所以你可以返回一个承诺数组,然后你可以等待他们的决议

在这种情况下,Promise.all 中的解析值将是您推入数组的值...即this.state.mediaPath

var completedMediaSurveysAnswers = [{}, {}, {}]; //object array of URLs and media types
var promises = completedMediaSurveysAnswers.map((i) => new Promise((resolve, reject) => {
    try {
        const storage = firebase.storage();
        const mRef = storage.ref('portal').child('Survey/Image/user/' + uuidv4() + 'media');
        mRef.putFile(i.urlPath, {
            contentType: i.mediaType
        }).on('state_changed', snapshot => {}, err => {
            console.log('Failed to upload file to firebase storage');
            resolve(null); // so one failure doesn't stop the whole process
        }, uploadedFile => {
            // Success
            this.setState({
                mediaPath: uploadedFile.downloadURL
            })
            resolve(this.state.mediaPath)
        });
    } catch (error) {
        console.log(error)
        resolve(null); // so one failure doesn't stop the whole process
    }
}));

//need to call this function after loop is done
Promise.all(promises).then(mediaArray => {
    saveAnswers(mediaArray);
});

【讨论】:

  • 在每次迭代中 this.state.mediaPath 应该推入 mediaArray 对吧?
  • 不正确 ... 承诺已解决 ... 然后 mediaArray 是承诺的数组结果 - 看看 mediaArray 如何在任何地方声明为变量,它是 .then 回调的参数
  • 所以这意味着从 resolve(this.state.mediaPath) 行它会一一添加所有的承诺,对吧?
  • 确实如此,并且数组结果将与completedMediaSurveysAnswers 的顺序相同 - 与原始代码中的顺序不同
  • 现在我明白了 :) 谢谢你的回答。干杯!!
【解决方案2】:

只需检查地图数组的长度并将其与地图键进行比较。

使用async await 等待文件上传

var mediaArray = []; //array of success callbacks values
var completedMediaSurveysAnswers = [{}, {}, {}]; //object array of URLs and media types
completedMediaSurveysAnswers.map(async (i, key) => {
  try {
    const storage = firebase.storage();
    const mRef = storage.ref('portal').child('Survey/Image/user/' + uuidv4() + 'media');
    await mRef.putFile(i.urlPath, {
        contentType: i.mediaType
      })
      .on('state_changed', snapshot => {},
        err => {
          console.log('Failed to upload file to firebase storage')
        },
        uploadedFile => {
          // Success
          this.setState({
            mediaPath: uploadedFile.downloadURL
          })
          mediaArray.push(this.state.mediaPath)
        });

     if( key == (completedMediaSurveysAnswers.length - 1 ) ){
       saveAnswers(mediaArray)
     }
  } catch (error) {
    console.log(error)
  }
})

我没有测试过async await,所以在任何设置的地方使用它。

【讨论】:

    【解决方案3】:

    您可以使用 Promise 或 async/await 来处理所有这些情况,如下所示:

    var mediaArray = [];
    var completedMediaSurveysAnswers = [{}, {}, {}];
    
    async function handleYourTask() {
      await completedMediaSurveysAnswers.map((i) => {
      try {
        const storage = firebase.storage();
        const mRef = storage.ref('portal').child('Survey/Image/user/' + uuidv4() + 'media');
        mRef.putFile(i.urlPath, {
            contentType: i.mediaType
          })
          .on('state_changed', snapshot => {},
            err => {
              console.log('Failed to upload file to firebase storage')
            },
            uploadedFile => {
              // Success
              this.setState({
                mediaPath: uploadedFile.downloadURL
              })
              mediaArray.push(this.state.mediaPath)
            });
        } catch (error) {
          console.log(error)
        }
      })
    
      await saveAnswers(mediaArray);
    }
    

    然后你可以在任何你想要的地方调用handleYourTask函数:)

    【讨论】:

    • 感谢您的回答。我也会试试这个:)
    【解决方案4】:

    据我所知,我们可以做类似下面的事情。

    学习: https://www.codegrepper.com/code-examples/javascript/wait+for+map+to+finish+javascript

    .map() 完成后执行操作:

    var promises = testUserArray.map((item, index) => {
        console.log('item', item)
    })
    
    await Promise.all(promises).then(() => {
        console.log('Map Opration Successfully Completed')
    })
    

    示例应用:

    import React, { useState } from 'react'
    import { View, StatusBar, Text, Button } from 'react-native'
    
    export default function App() {
    
        const [testUserArray, setTestUserArray] = useState(testArray)
    
        const _testMap = async () => {
    
            var promises = testUserArray.map((item, index) => {
                console.log('item', item)
            })
    
            await Promise.all(promises).then(() => {
                console.log('Map Opration Successfully Completed')
            })
        }
        return (
            <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', }}>
                <Button
                    title='Test .map()'
                    onPress={() => _testMap()}
                />
            </View>
        )
    }
    
    const testArray = [
        {
            "id": '1',
            "name": 'User 1'
        },
        {
            "id": '2',
            "name": 'User 2'
        },
        {
            "id": '3',
            "name": 'User 3'
        },
        {
            "id": '4',
            "name": 'User 4'
        },
        {
            "id": '5',
            "name": 'User 5'
        },
    ]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-01
      • 1970-01-01
      • 2018-08-28
      • 1970-01-01
      • 2014-03-22
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      相关资源
      最近更新 更多