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

在JavaScript中使用承诺创建异步函数,javascript,node.js,asynchronous,promise,axios,Javascript,Node.js,Asynchronous,Promise,Axios,我有一个函数 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'

我有一个函数

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 '';
  });
}
它下载一个远程图像并上传到AmazonS3。我希望它返回生成的密钥

我想使用这样的函数

const images = ['http://...', 'http://...', ...].map((url, i) => {
  return {
    url: getImage(url, i)
  }
});
由于我的函数
getImage()
对于每个URL都可能需要一段时间,因此我想我必须使用异步调用,以便在继续下一个元素之前(或者我误解了什么?)

我想我必须使用承诺,所以解决方案可能是这样的吗

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 用例是我从一个公共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);
    })
  };
});
那么,我将如何构造博客文章数组的格式呢?问题是,如果发生错误,我不想将图像包含在图像数组中。我不知道如何用承诺来检验这一点。

使用ECMAScript 2017语法,您可以非常轻松地完成这一点。修改脚本的原始形式(您声称它正在工作),它将如下所示:

异步函数getImage(url,键){ 试一试{ 常数响应=等待axios({ 方法:“get”, 网址, 响应类型:'stream' }) 等待s3.0上传({ 钥匙:钥匙, 正文:response.data, ContentType:response.data.headers['content-type'], ACL:“公共读取” }).承诺 }捕获(错误){ //返回错误 return key//因为这里没有axios和s3 } 返回键 } 常量blogpostoriginal=[ {title:'title',图像:['url1','url2']}, {title:'title',图像:['url1','url2']}, {title:'title',图像:['url1','url2']}, ]; Promise.all(blogpostoriginal.map)(异步({title,images})=>{ 返回{ 标题 images:(等待Promise.all(images.map)(异步(url)=>{ //把你的钥匙拿过来 const key=Math.random().toString(36).slice(2,12).padStart(10,0) //将图像上载到S3 返回getImage(url,键) }))) //等待结果数组后,过滤掉错误 .filter(结果=>!(错误的结果实例)) } }))。然后(blogPostsFormatted=>{ //使用此处格式的BlogPosts console.log(blogpostsformated) })使用ECMAScript 2017语法,您可以非常轻松地完成此任务。修改脚本的原始形式(您声称它正在工作),它将如下所示:

异步函数getImage(url,键){ 试一试{ 常数响应=等待axios({ 方法:“get”, 网址, 响应类型:'stream' }) 等待s3.0上传({ 钥匙:钥匙, 正文:response.data, ContentType:response.data.headers['content-type'], ACL:“公共读取” }).承诺 }捕获(错误){ //返回错误 return key//因为这里没有axios和s3 } 返回键 } 常量blogpostoriginal=[ {title:'title',图像:['url1','url2']}, {title:'title',图像:['url1','url2']}, {title:'title',图像:['url1','url2']}, ]; Promise.all(blogpostoriginal.map)(异步({title,images})=>{ 返回{ 标题 images:(等待Promise.all(images.map)(异步(url)=>{ //把你的钥匙拿过来 const key=Math.random().toString(36).slice(2,12).padStart(10,0) //将图像上载到S3 返回getImage(url,键) }))) //等待结果数组后,过滤掉错误 .filter(结果=>!(错误的结果实例)) } }))。然后(blogPostsFormatted=>{ //使用此处格式的BlogPosts console.log(blogpostsformated)
})当您调用
getImage
时,它会通过
axios
创建一个承诺。如果您想对不同的URL多次使用它,那么您就可以了,因为每个承诺都会在解决后返回。在这种情况下,在Promise对象中包装
axios
是多余的。啊,我明白了。我已经更新了我的问题,当您调用
getImage
时,它通过
axios
创建承诺。如果您想对不同的URL多次使用它,那么您就可以了,因为每个承诺都会在解决后返回。在这种情况下,在Promise对象中包装
axios
是多余的。啊,我明白了。我已经更新了我的问题,谢谢!看起来不错,但我仍然不确定如何用我的用例实现它。在我的第二次编辑中,你能帮我回答我的问题吗?另一个简短的问题。您的原始代码在未获取或上载的图像上以静默方式失败,并返回一个空白字符串作为密钥。修改后的代码返回单个承诺,这些承诺根据其成功与否而定,但您没有任何东西等待它们集体实现,这就是为什么我使用了
Promise.all()
,但如果其中任何一个失败,这将导致所有承诺都失败。这样可以吗?如果一个映像失败,我只希望它从阵列中删除。因此,我将尽可能多地保留图像。我想如果出现这种情况,我将不得不返回
null
,然后使用
.filter(element=>!!element)
或类似的方法来删除那些不起作用的内容,但是当我使用
图像时,这似乎有点像黑客:(等待Promise.all(blogPost.images.map(async(url)=>getImage(url)).filter(result=>(result instanceof Error)),
,我得到了
语法错误:意外标识符
注意我答案中
wait
周围的括号。这些是必须的,谢谢!看起来不错,但我仍然不确定如何用我的用例实现它。在我的第二次编辑中,你能帮我回答我的问题吗?另一个简短的问题。您的原始代码在未获取或上载的图像上以静默方式失败,并返回一个空白字符串作为密钥。修改后的代码返回单个承诺,这些承诺根据成功与否而定,但您没有任何东西等待它们的实现