mrousavy / react-native-blob-jsi-helper

A React Native library for accessing an ArrayBuffer of a Blob instance.
MIT License
131 stars 8 forks source link

Bytes aren't synchronously ready when calling getArrayBufferForBlob immediately after Blob.new() #14

Open shamilovtim opened 7 months ago

shamilovtim commented 7 months ago

We're experiencing a bug with this package where getArrayBufferForBlob() does not account for the bytes of the Blob not being available yet after Blob.new() is called. This seems to indicate that a synchronous API somewhere is not actually synchronous. I have not dug into the C++ to figure out how that's possible. We have a JS based solution but it's inelegant.

Repro:

const foo = new Blob(['foo']);
const arrayBuffer = getArrayBufferForBlob(foo); 
// tried to getArrayBufferForBlob but the bytes of the Blob weren't ready 

JS based patch (with overhead):

/**
 * Patches bug caused by calling getArrayBufferForBlob() right after constructing a new Blob()`.
 *
 * In React Native, Blobs are created via NativeBlobModule. Despite the API to
 * create Blobs being synchronous, the actual bytes backing the Blob may not be
 * available immmediately.
 *
 * This function looks for the backing arrayBuffer for a Blob, with small microsleeps
 * until it is ultimately available via the JSI.
 */
async function getArrayBuffer(blob: Blob): Promise<Uint8Array> {
  let arrayBuffer: Uint8Array | undefined = undefined;
  try {
    arrayBuffer = getArrayBufferForBlob(blob);
  } catch {}

  if (arrayBuffer && arrayBuffer.length === blob.size) {
    return arrayBuffer;
  } else {
    // The buffer isn't available yet from the JSI.
    // Microsleep to advance to the next runloop, and try again.
    await sleep(1);
    return getArrayBuffer(blob);
  }
}

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
mrousavy commented 7 months ago

maybe the Blob API is asynchronous?