简体   繁体   中英

Make asynchronous function with promises in JavaScript

I have a function

function getImage(url, key) {
  return axios({
    method: 'get',
    url,
    responseType: 'stream'
  }).then(response => {
    s3.upload({
      Key: key,
      Body: response.data,
      ContentType: response.data.headers['content-type'],
      ACL: 'public-read'
    }, (err, data) => {
      return key
    });
  }).catch(err => {
    return '';
  });
}

which downloades a remote image and uploads it to Amazon S3. I want it to return the generated key.

I want to use the function like this

const images = ['http://...', 'http://...', ...].map((url, i) => {
  return {
    url: getImage(url, i)
  }
});

Since my function getImage() could take a little while for every single URL, I guess I will have to use asynchronous calls so that I'm sure that the function is completely done before moving on to the next element (or am I misunderstanding something?).

I guess I have to use promises, so could a solution be something like this?

function getImage(url, key) {
  return new Promise((resolve, reject) => {
    return axios({
      method: 'get',
      url,
      responseType: 'stream'
    }).then(response => {
      s3.upload({
        Key: key,
        Body: response.data,
        ContentType: response.data.headers['content-type'],
        ACL: 'public-read'
      }, (err, data) => {
        resolve(key);
      });
    }).catch(err => {
      reject(err);
    });
  });
}

and then use it like this:

const images = ['http://...', 'http://...', ...].map((url, i) => {
  return {
    url: getImage(url, i).then(url => url).catch(err => [])
  }
});

Edit

As mentioned in the comments, axios is a promise. Should the code then look like

function getImage(url, key) {
  return axios({
    method: 'get',
    url,
    responseType: 'stream'
  }).then(response => {
    return new Promise((resolve, reject) => {
      s3.upload({
        Key: key,
        Body: response.data,
        ContentType: response.data.headers['content-type'],
        ACL: 'public-read'
      }, (err, data) => {
        if (!err) {
          resolve(key);
        } else {
          reject(err);
        }
      });
    });
  });
}

Edit 2

The use case is that I'm fetching a lot of blog posts from a public API. So I am doing something like

const blogPostsOriginal = [
  { title: 'Title', images: ['url1', 'url2'] },
  { title: 'Title', images: ['url1', 'url2'] },
  { title: 'Title', images: ['url1', 'url2'] },
];

const blogPostsFormatted = blogPostsOriginal.map(blogPost => {
  return {
    title: blogPost.title,
    images: blogPost.images.map(url => {
      // upload image to S3
      return getImage(url);
    })
  };
});

So how would I structure the formatting of the array of blog posts? The problem is that if an error happened, I don't want to include the image in the array of images. I am not sure how to check this with promises.

Using ECMAScript 2017 async / await syntax, you can quite easily accomplish this. Modifying the original form of your script, which you claim is working, it would look like the following:

 async function getImage(url, key) { try { const response = await axios({ method: 'get', url, responseType: 'stream' }) await s3.upload({ Key: key, Body: response.data, ContentType: response.data.headers['content-type'], ACL: 'public-read' }).promise() } catch (error) { // return error return key // since we don't have axios and s3 in here } return key } const blogPostsOriginal = [ { title: 'Title', images: ['url1', 'url2'] }, { title: 'Title', images: ['url1', 'url2'] }, { title: 'Title', images: ['url1', 'url2'] }, ]; Promise.all(blogPostsOriginal.map(async ({ title, images }) => { return { title, images: (await Promise.all(images.map(async (url) => { // get your key here somehow const key = Math.random().toString(36).slice(2, 12).padStart(10, 0) // upload image to S3 return getImage(url, key) }))) // after awaiting array of results, filter out errors .filter(result => !(result instanceof Error)) } })).then(blogPostsFormatted => { // use blogPostsFormatted here console.log(blogPostsFormatted) }) 

To explain the bit about s3.upload(...).promise() , I got that from the documentation here and here .

References

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM