使用 JavaScript 中的承诺创建异步函数

Make asynchronous function with promises in JavaScript

我有一个功能

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 '';
  });
}

下载远程图像并将其上传到 Amazon S3。我想要它 return 生成的密钥。

我想使用这样的功能

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

因为我的函数 getImage() 对每个函数 URL 都可能需要一点时间,我想我将不得不使用异步调用,这样我才能确定该函数在移动之前已完全完成进入下一个元素(或者我误解了什么?)。

我想我必须使用 promises,那么解决方案可能是这样的吗?

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);
    });
  });
}

然后像这样使用它:

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

编辑

正如评论中所说,axios是一个承诺。代码应该看起来像

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);
        }
      });
    });
  });
}

编辑 2

用例是我从 public API 中获取大量博客文章。所以我正在做类似

的事情
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);
    })
  };
});

那么我将如何构建博客文章数组的格式?问题是如果发生错误,我不想将图像包含在图像数组中。我不确定如何用 promises 检查这个。

使用 ECMAScript 2017 async / await 语法,您可以轻松完成此操作。修改您声称有效的脚本的原始形式,它将如下所示:

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)
})

为了解释有关 s3.upload(...).promise() 的部分,我从文档 here and here 中得到了它。

参考资料