0

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

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

❌ 应用界面被阻塞

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

谁能帮我解决这个问题?


反应原生 fs”:“2.13.3

反应原生:0.59.9

4

2 回答 2

4

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);
} 
于 2019-08-22T01:26:26.510 回答
1

are you using some kind of state management? Redux, context, redux-saga/redux-thunk? You can use react-native-background-downloader to try and download through background.

If you are using redux and redux-saga you can download it using fork so it would be non-blocking.

Also, if you are downloading images and storing it or it's path in props or state, it probably is rerendering for each image downloaded.

于 2019-08-22T00:06:07.363 回答