【问题标题】:DownloadFile with thousands of files is blocking the application interface包含数千个文件的 DownloadFile 阻塞了应用程序界面
【发布时间】:2019-08-21 22:30:26
【问题描述】:

我正在使用以下代码下载 JPG 格式的 15019 张图片:

for (const item of requestUrl) {
    const optios = {
        fromUrl: item.urlPath,
        toFile: item.path,
    };
    downloadFile(optios).promise;
}

❌应用界面被屏蔽

注意事项:我知道 RN 只在一个线程上运行,所以我实现了在前台和后台运行的 Headless JS,它可以工作,但同样的行为,接口是阻塞的。

谁能帮我解决这个问题?


react-native-fs": "2.13.3

反应原生:0.59.9

【问题讨论】:

    标签: react-native react-native-fs


    【解决方案1】:

    react-native-fs 在它的原生实现中完成所有工作,并通过 react-native 桥暴露一个异步 api。因此,通过 react-native-js 下载文件时,您的 js 线程大部分时间都是空闲的,无论您的 js 是在前台运行还是在后台运行。

    实际问题

    您的实现的实际问题是downloadFile 将触发下载并立即返回一个 Promise。因此,您的 for 循环将几乎立即运行所有 15k 次迭代,在应用的本机部分开始 15k 次并发下载。这可能足以减慢任何设备的速度,以至于整个应用程序都被阻止了。

    解决方案

    如果你需要下载很多文件,你应该实现某种队列和流水线来限制并发下载的数量。 (即使设备可以毫无问题地处理 15k 并发下载,下载也会受到可用带宽的限制。大约 3 次并发下载可能足以一直使用最大带宽。)

    可能的流水线解决方案如下所示:

    (以下代码未运行或测试)

    
    /**
     * 
     * @param {item} The item to download
     * @param {item.urlPath} The url to download the item from
     * @param {item.path} The local path to download the item to
     * @returns the Promise returned by react-native-fs downloadFile
     */
    const downloadItem = ({ urlPath, path }) => {
      const options = {
        fromUrl: urlPath,
        toFile: path
      }
      return downloadFile(options);
    }
    
    /**
     * 
     * @param {Array} Array of the items to download
     * @param {integer} maximum allowed concurrent downloads
     * @returns {Promise} which resolves when all downloads are done
     */
    const downloadPipelined = (items, maxConcurrency = 3) => {
        // Clone the items Array because we will mutate the queue
        const queue = [...items];
        /**
         * Calling this function will
         * - donwload one item after another until the queue is empty
         * - return a promise which resolves if the queue is empty and
         *   the last download started from this workerFn is finished
         */
        const workerFn = async () => {
            while (queue.length > 0){
                await downloadItem(queue.shift()); 
            }
        }
        /**
         * starts maxConcurrency of these workers concurrently
         * and saves the promises returned by them to an array
         */ 
        const workers = Array(maxConcurrency).fill(workerFn());
        /**
         * will resolve when all worker promises have resolved
         * (all downloads are done)
         * reject if one rejects
         */
        return Promise.all(workers);
    } 
    

    【讨论】:

      【解决方案2】:

      您是否在使用某种状态管理? Redux,上下文,redux-saga/redux-thunk? 您可以使用react-native-background-downloader尝试后台下载。

      如果你使用的是 redux 和 redux-saga,你可以使用 fork 下载它,这样它就不会阻塞了。

      此外,如果您正在下载图像并存储它,或者它的路径在 props 或 state 中,它可能正在为每个下载的图像重新渲染。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-22
        • 2017-07-30
        • 1970-01-01
        • 2016-05-18
        相关资源
        最近更新 更多