gildas-lormeau / zip.js

JavaScript library to zip and unzip files supporting multi-core compression, compression streams, zip64, split files and encryption.
https://gildas-lormeau.github.io/zip.js
BSD 3-Clause "New" or "Revised" License
3.38k stars 510 forks source link

The 'closed' Promise Always in a pending state in Chromium 78 #426

Closed shay-an closed 1 year ago

shay-an commented 1 year ago

Help Me!

My code:

var zipWriter = new zip.ZipWriter(new zip.BlobWriter('application/zip'))
    await Promise.all(
      _.map(files, async (file) => {
        await zipWriter.add(file.name, new zip.BlobReader(file), {
          bufferedWrite: true,
          password: password,
          signal,
          zipCrypto: true,
          onprogress: (index, max) => {
          },
        })
      })
    )

// Not running at here
 let data = await zipWriter.close()
 console.log(data)

Running on Chrome is normal After my debugging, I have located here: At /lib/core/codec-worker.js

function watchClosedStream(writableSource) {
    const writer = writableSource.getWriter();
    let resolveStreamClosed;
    const closed = new Promise(resolve => resolveStreamClosed = resolve);
    const writable = new WritableStream({
        async write(chunk) {
            await writer.ready;
            await writer.write(chunk);
        },
        close() {
                        // Not running at here
            writer.releaseLock();
            resolveStreamClosed();
        },
        abort(reason) {
            return writer.abort(reason);
        }
    });
    return { writable, closed };
}

The browser download URL https://browser.360.cn/ee/mac/index.html System : Mac os 10.15.7 (19H15)

gildas-lormeau commented 1 year ago

I tried to reproduce your bug with the latest version of 360 speed browser. The test can be found here: https://plnkr.co/edit/HwnrHqehd05quud3?open=lib%2Fscript.js&preview.

Here is the code of the test below.

<!doctype html>

<html>

<head>
  <title>Adding concurrently multiple entries in a zip file</title>
</head>

<body>
  <script type="module">

    import * as zip from "https://unpkg.com/@zip.js/zip.js/index.js";

    const MB = 1024 * 1024;
    const ENTRIES_DATA = [
      { name: "entry #1", blob: getBlob(8.5 * MB) },
      { name: "entry #2", blob: getBlob(5.2 * MB) },
      { name: "entry #3", blob: getBlob(4.7 * MB) },
      { name: "entry #4", blob: getBlob(2.8 * MB) },
      { name: "entry #5", blob: getBlob(1.9 * MB) },
      { name: "entry #6", blob: getBlob(2.2 * MB) },
      { name: "entry #7", blob: getBlob(5.1 * MB) },
      { name: "entry #8", blob: getBlob(2.6 * MB) },
      { name: "entry #9", blob: getBlob(3.1 * MB) },
      { name: "entry #10", blob: getBlob(8.5 * MB) },
      { name: "entry #11", blob: getBlob(5.2 * MB) },
      { name: "entry #12", blob: getBlob(4.7 * MB) },
      { name: "entry #13", blob: getBlob(2.8 * MB) },
      { name: "entry #14", blob: getBlob(1.9 * MB) },
      { name: "entry #15", blob: getBlob(2.2 * MB) },
      { name: "entry #16", blob: getBlob(5.1 * MB) },
      { name: "entry #17", blob: getBlob(2.6 * MB) },
      { name: "entry #18", blob: getBlob(3.1 * MB) }];

    function getBlob(size) {
      const data = new Float64Array(Math.floor(size / 8));
      for (let indexData = 0; indexData < data.length; indexData++) {
        data[indexData] = Math.random();
      }
      return new Blob([data]);
    }

    async function test() {
      const controller = new AbortController();
      const signal = controller.signal;
      const blobWriter = new zip.BlobWriter("application/zip");
      const zipWriter = new zip.ZipWriter(blobWriter);
      await Promise.all(ENTRIES_DATA.map(entryData =>
        zipWriter.add(entryData.name, new zip.BlobReader(entryData.blob), {
          bufferedWrite: true,
          password: "password",
          signal,
          zipCrypto: true,
          onprogress: (index, max) => {
          }
        })));
      await zipWriter.close();
      document.body.innerHTML = "OK";
    }

    test();

  </script>
</body>

</html>

On my end, "OK" is always displayed in the page and everything works as expected. What changes should be applied to this code in order to reproduce your bug?

shay-an commented 1 year ago

Hello! My browser version is 12.2.1664.0 Chromium core version is 78

https://plnkr.co/edit/IsuSY533fDonkC8R Here is the code of the test below.

<!doctype html>

<html>

<head>
  <title>Adding concurrently multiple entries in a zip file</title>
</head>

<body>
  <script type="module">

    import * as zip from "https://unpkg.com/@zip.js/zip.js/index.js";

    const MB = 1024 * 1024;
    const ENTRIES_DATA = [
      { name: "entry #1", blob: getBlob(8.5 * MB) },
      { name: "entry #2", blob: getBlob(5.2 * MB) },
      { name: "entry #3", blob: getBlob(4.7 * MB) },
      { name: "entry #4", blob: getBlob(2.8 * MB) },
      { name: "entry #5", blob: getBlob(1.9 * MB) },
      { name: "entry #6", blob: getBlob(2.2 * MB) },
      { name: "entry #7", blob: getBlob(5.1 * MB) },
      { name: "entry #8", blob: getBlob(2.6 * MB) },
      { name: "entry #9", blob: getBlob(3.1 * MB) },
      { name: "entry #10", blob: getBlob(8.5 * MB) },
      { name: "entry #11", blob: getBlob(5.2 * MB) },
      { name: "entry #12", blob: getBlob(4.7 * MB) },
      { name: "entry #13", blob: getBlob(2.8 * MB) },
      { name: "entry #14", blob: getBlob(1.9 * MB) },
      { name: "entry #15", blob: getBlob(2.2 * MB) },
      { name: "entry #16", blob: getBlob(5.1 * MB) },
      { name: "entry #17", blob: getBlob(2.6 * MB) },
      { name: "entry #18", blob: getBlob(3.1 * MB) }];

    function getBlob(size) {
      const data = new Float64Array(Math.floor(size / 8));
      for (let indexData = 0; indexData < data.length; indexData++) {
        data[indexData] = Math.random();
      }
      return new Blob([data]);
    }

    async function test() {
      const controller = new AbortController();
      document.body.innerHTML = "1";
      const signal = controller.signal;
      document.body.innerHTML = "2";
      const blobWriter = new zip.BlobWriter("application/zip");
      document.body.innerHTML = "3";
      const zipWriter = new zip.ZipWriter(blobWriter);
      document.body.innerHTML = "4";
      await Promise.all(ENTRIES_DATA.map(entryData =>
        zipWriter.add(entryData.name, new zip.BlobReader(entryData.blob), {
          bufferedWrite: true,
          password: "password",
          signal,
          zipCrypto: true,
          onprogress: (index, max) => {
          }
        })));
      document.body.innerHTML = "5";
      await zipWriter.close();
      document.body.innerHTML = "OK";
    }

    test();

  </script>
</body>

</html>

Always display "4"

But I upgraded my browser to the latest version, "OK" is always displayed in the page and everything works as expected. This new browser version 14.5.0000.0 Chromium core version is 108

Does it not support versions as low as 78?

gildas-lormeau commented 1 year ago

Thank you for the additional info, I was able to reproduce the bug in Chromium 78. The version 2.7.16 that I've just published should fix the issue.