How to wait two promises before returning to function?

Asked

Viewed 461 times

0

I am making a code that makes two requests, the value returned is the size of a file content-length, but I need the function that contains these two requests to return the value after the requests are finished (Yes, they take a while). But I don’t know how to handle this demand for requisitions.

Being more detailed, when I call the function: addItem the function saveFiles will be called, within this function will be called two more functions (sometimes only one depending on the type of media) _getImage and _getVideo, these two functions will download the media and save on mobile and return the file size. Put inside saveFiles functions are returning before they even finish, so I don’t have the size value when I need it.

I tried to use the async and the await, but I have not succeeded, yet my duties are not waiting. Is there any way to wait for a promise to finish and then execute the promised step?

import RNFetchBlob from 'rn-fetch-blob';

export default class Manager {
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.listOfFiles = [];
    this.dirs = RNFetchBlob.fs.dirs;
  }

  addItem(item, media) {
    size = this.saveFiles(item);
    console.log(size);
    this.listOfFiles.push({
      inFeed: false,
      media: media,
    })
  }

  seeFiles() {
    for(i = 0 ; i < this.listOfFiles.length; i++) {
      console.log(this.listOfFiles[i])
    }
  }

  saveFiles(item) {
    var sizeImage = 0;
    var sizeVideo = 0;

    if(item.type == 'Animated') {
      sizeVideo = this._getVideo(item.images.image460svwm.url);
      sizeImage = this._getImage(item.images.image460.url);
    } else {
      sizeImage = this._getImage(item.images.image460.url);
    }

    return sizeImage + sizeVideo;
  }

  async _getImage(url) {
    var filename = url.substring(url.lastIndexOf('/')+1);
    return await RNFetchBlob.config({
      // add this option that makes response data to be stored as a file,
      // this is much more performant.
      fileCache : true,
      path : this.dirs.DocumentDir + '/media/' + filename
    })
    .fetch('GET', url, {
      //some headers ..
    })
    .then((res) => {
      console.log(res.respInfo.headers['content-length'])
      return res.respInfo.headers['content-length']
    })
  }

  async _getVideo(url) {
    var filename = url.substring(url.lastIndexOf('/')+1);
    return await RNFetchBlob.config({
      // add this option that makes response data to be stored as a file,
      // this is much more performant.
      fileCache : true,
      path : this.dirs.DocumentDir + '/media/' + filename
    })
    .fetch('GET', url, {
      //some headers ..
    })
    .then((res) => {
      console.log(res.respInfo.headers['content-length'])
      return res.respInfo.headers['content-length']
    })
  }
}

2 answers

0

the only way to expect a Promise is by using async/await if your methods _getVideo and _getImage return a sufficient Promise in your method saveFiles do:

async saveFiles(item) {
    var sizeImage = 0;
    var sizeVideo = 0;

    if(item.type == 'Animated') {
        sizeVideo = await this._getVideo(item.images.image460svwm.url);
        sizeImage = await this._getImage(item.images.image460.url);
    } else {
        sizeImage = await this._getImage(item.images.image460.url);
    }

    return sizeImage + sizeVideo;
}

I would also give you a tip, to start using Let or const instead of var: https://medium.com/collabcode/javascript-e-suas-vari%C3%A1veis-var-Let-e-const-b035b44c2dab

0

You must keep in mind that both _getImage how much _getVideo are functions that return Promises. Therefore, you must wait for your resolution from the function saveFiles. How do you want to expect two promises are solved, the simplest way to do this is to use Promise.all, which awaits the resolution of all Promises passed.

Thus:

saveFiles(item) {
  // Inicializamos o `array` com as funções que desejamos que sejam executadas.
  const promises = [this._getImage(item.images.image460.url)];

  // Se o item for do `type` `'Animated'`, adicionamos mais um item à lista:
  if (item.type === 'Animated') {
    promises.push(this._getVideo(item.images.image460svwm.url));
  }

  // Aguardamos que todas as `Promise`s  sejam executadas:
  return Promise.all(promises).then(([sizeImage, sizeVideo]) => {
    return sizeImage + (sizeVideo || 0);
  });
}

The advantage of using the Promise.all is that he executes all the Promises in parallel, so the result will be almost twice as fast, since one does not depend on the other. :)

Note that the method saveFiles will yet return a Promise, so, to get the result, use the then. You’ll probably want to do this on componentDidMount:

componentDidMount() {
  //              ↓↓↓↓
  saveFiles(item).then((sumOfSizes) => {
    this.setState({ sumOfSizes })
  })
}

You can even match the async/await with the Promise.all, getting along:

async saveFiles(item) {
  // Inicializamos o `array` com as funções que desejamos que sejam executadas.
  const promises = [this._getImage(item.images.image460.url)];

  // Se o item for do `type` `'Animated'`, adicionamos mais um item à lista:
  if (item.type === 'Animated') {
    promises.push(this._getVideo(item.images.image460svwm.url));
  }

  // Aguardamos que todas as `Promise`s  sejam executadas:
  const [sizeImage, sizeVideo] = await Promise.all(promises);

  // Retornamos a soma:
  return sizeImage + (sizeVideo || 0);
}

async componentDidMount() {
  const sumOfSizes = await saveFiles(item);
  this.setState({ sumOfSizes });
}
  • It is important to point out that: If any of the past promises are rejected, the Promise.all rejects asynchronously with the value of the promise that was rejected

Browser other questions tagged

You are not signed in. Login or sign up in order to post.