0

I'm building a chunked file uploader. I understood that promises are the best way to handle the results but still, can't wrap my mind of how I should capture the promises from the outside.

caller.js

let fileUploader = new ChunkedUpload()
    fileUploader.setFile(file)
    fileUploader.upload().then(result => {
       // Here I'd like to catch the current result of the file
       // If it's not uploaded, I need the percentage and continue 
       // If it is I need the response from the API
       console.log(result)
    })

uploader.js

upload() {
    let totalChunks = Math.ceil(this._file.size/this._sliceSize)

    return this._chunk(0, 0, totalChunks)
}

_chunk(start, chunk, totalChunks) {
    let lastChunk = false
    let end = start + this._sliceSize

    if (this._file.size - end < 0) {
        end = this._file.size
        lastChunk = true
    }

    let slice = this._sliceFile(this._file, start, end)

    let formData = new FormData()
    formData.append('chunk', chunk)
    formData.append('chunks', totalChunks)
    formData.append('file', slice)

    return new Promise((resolve, reject) => {
        axios.post(this._url, formData).then(response => {
            if (lastChunk) {
                // This part is okay, it returns the result
                resolve({
                    uploaded: true,
                    url: response.data,
                    uploadPercentage: 100,
                    uploadSize: this.formatBytes(file.size)
                });

            } else {
                // Here's the issue. Next chunk upload is called, 
                // however I cannot track the process in caller.js
                resolve(this._chunk(
                    start += this._sliceSize,
                    chunk += 1,
                    totalChunks
                ));
            }
        })
    })
}

I accept any comments and whatsoever. Maybe my approach is wrong, please let me know!

Dimitar Mitov
  • 37
  • 2
  • 7
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Feb 28 '18 at 11:04

2 Answers2

1

Promise should be used to ONE-TIME callback - success or error.

What you want is "progress" information.

That way you should:

  • Use a callback function instead to get details about progress; or
  • Listen and emit events;

BUT, if you really want to use promise and not use callbacks or events, I would suggest:

return promise with details AND a method inside called continue() that HAS to be called so the process may continue.

Below I give you the code for that:

caller.js

let fileUploader = new ChunkedUpload()
    fileUploader.setFile(file)
    var callback = result => {
       // Here I'd like to catch the current result of the file
       // If it's not uploaded, I need the percentage and continue 
       // If it is I need the response from the API
       console.log(result);
       if (result.uploaded) {
           console.log('DONE');
       } else {
           console.log('STILL UPLOADING...');
           result.continue()
               .then(callback);
       }
    }
    fileUploader.upload().then(callback);

uploader.js

upload() {
    let totalChunks = Math.ceil(this._file.size/this._sliceSize)

    return this._chunk(0, 0, totalChunks)
}

_chunk(start, chunk, totalChunks) {
    let lastChunk = false
    let end = start + this._sliceSize

    if (this._file.size - end < 0) {
        end = this._file.size
        lastChunk = true
    }

    let slice = this._sliceFile(this._file, start, end)

    let formData = new FormData()
    formData.append('chunk', chunk)
    formData.append('chunks', totalChunks)
    formData.append('file', slice)

    return new Promise((resolve, reject) => {
        axios.post(this._url, formData).then(response => {
            if (lastChunk) {
                // This part is okay, it returns the result
                resolve({
                    uploaded: true,
                    url: response.data,
                    uploadPercentage: 100,
                    uploadSize: this.formatBytes(file.size)
                });

            } else {
                // Here's the issue. Next chunk upload is called, 
                // however I cannot track the process in caller.js
                // you may include in the object below the metrics you need
                resolve({
                    uploaded: false,
                    continue: () => {
                      return this._chunk(
                          start += this._sliceSize,
                          chunk += 1,
                          totalChunks
                    }
                });
            }
        })
    })
}
Rafael Paulino
  • 570
  • 2
  • 9
0

Thanks @Rafel! Your answer led me to events.

Apparently I got caught into the Promise construct antipattern as @Bergi mentioned and catching a reject would've resulted into another variable definition.

Thanks everyone!

Dimitar Mitov
  • 37
  • 2
  • 7