react-native-fs は、そのネイティブ実装ですべての作業を行い、反応ネイティブ ブリッジを介して非同期 API を公開します。したがって、js スレッドは、react-native-js を介してファイルをダウンロードしている間、ほとんどの場合アイドル状態であり、js がフォアグラウンドで実行されているかバックグラウンドで実行されているかは問題ではありません。
実際の問題
実装の実際の問題はdownloadFile
、ダウンロードをトリガーして Promise を即座に返すことです。したがって、for ループは 15,000 回の反復すべてをほぼ瞬時に実行し、アプリのネイティブ部分で15,000 回の同時ダウンロードを開始します。これはおそらく、アプリ全体がブロックされるほどデバイスの速度を低下させるのに十分です.
解決
多くのファイルをダウンロードする必要がある場合は、何らかのキューとパイプラインを実装して、同時ダウンロード数を制限する必要があります。(デバイスが 15,000 の同時ダウンロードを問題なく処理できたとしても、ダウンロードは利用可能な帯域幅によって制限されます。最大帯域幅を常に使用するには、最大 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);
}