Node.js NodeJS流行为管道vs承诺

Node.js NodeJS流行为管道vs承诺,node.js,promise,node-streams,Node.js,Promise,Node Streams,我正在实现一些代码来获取图像,使用库将其转换为两种格式png和jpg,并返回这两种流,以便稍后上传到S3 bucket 我提供了两种不同的解决方案,一种使用Promise,另一种使用stream.pipeline。 然而,由于某些原因,管道版本的运行速度比承诺的慢得多 下面是重现行为的代码(使用节点14运行) const sharp=require('sharp')) 常量fs=require('fs') const util=require('util') const stream=requi

我正在实现一些代码来获取图像,使用库将其转换为两种格式png和jpg,并返回这两种流,以便稍后上传到S3 bucket

我提供了两种不同的解决方案,一种使用Promise,另一种使用stream.pipeline。 然而,由于某些原因,管道版本的运行速度比承诺的慢得多

下面是重现行为的代码(使用节点14运行)

const sharp=require('sharp'))
常量fs=require('fs')
const util=require('util')
const stream=require('stream')
const pipeline=util.promisify(stream.pipeline);
console.time('resize')
常量resizeJobPipeline=async(readableStream)=>{
常数sharpStream=夏普({
失败者:错误
}).resize({宽度:800,高度:800,适合:'内部'})
//在这里使用PassThrough,因为在最后的代码中必须将此流传递给s3上载
const memoryPng=new stream.PassThrough()
const memoryJpg=new stream.PassThrough()
//必须单独等待每条管道,
//如果将它们包装在Promise.all中,则图像不会得到完全处理/损坏
等待管道(readableStream、sharpStream.clone().png()、memoryPng)
等待管道(readableStream、sharpStream.clone().jpeg()、memoryJpg)
return[memoryPng,memoryJpg]
}
常量resizeJobPromise=async(readableStream)=>{
常数sharpStream=夏普({
失败者:错误
}).resize({宽度:800,高度:800,适合:'内部'})
常量承诺=[]
promises.push(sharpStream.clone().png().pipe(new stream.PassThrough()))
promises.push(sharpStream.clone().jpeg().pipe(new stream.PassThrough()))
readableStream.pipe(sharpStream)
返回等待承诺。全部(承诺)
}
const readStream=fs.createReadStream('big_img.jpg'))
//resizeJobPromise(readStream)。然后(res=>{
//res[0]。管道(fs.createWriteStream('resized.png'))
//res[1]。管道(fs.createWriteStream('resized.jpg'))
//console.timeEnd('调整大小')
//})。捕获(错误=>{
//console.log(错误)
// })
resizeJobPipeline(readStream)。然后(res=>{
res[0]。管道(fs.createWriteStream('resized.png'))
res[1]。管道(fs.createWriteStream('resized.jpg'))
console.timeEnd('调整大小')
}).catch(错误=>{
console.log(错误)
})
如果我运行resizeJobPipeline版本,使用大约20mb的映像,平均执行时间约为500ms

然而,如果评论这个版本并运行resizeJobPromise版本,使用相同的图像,我得到的平均时间只有~7毫秒

通过按顺序等待两条管道,我预计可能会得到双倍的时间,但不是100倍

我读到管道版本使用起来更安全,因为它会自动处理可读数据流上的错误,并关闭可写数据流以防止内存泄漏,而在promise版本上,我必须手动处理这些错误

我的承诺版本有什么不对吗?代码背后会发生什么事情使其具有如此高的性能

我的承诺版本有什么不对吗

是的,您没有测量流的执行时间。注意

promises.push(sharpStream.clone().png().pipe(new stream.PassThrough()))
promises.push(sharpStream.clone().jpeg().pipe(new stream.PassThrough()))
只需将流对象推送到一个数组中,将它们传递给
Promise。所有
都不会等待流完成,而是立即满足流对象的要求。您也可以从这个函数中省略promise内容

您应该做的是将流
管道化
到文件/s3中写入流:

const sharp = require('sharp')
const fs = require('fs')
const util = require('util')
const stream = require('stream')
const pipeline = util.promisify(stream.pipeline)

function resizeJob() {
  const sharpStream = sharp({
    failOnError: false
  }).resize({width: 800, height: 800, fit: 'inside'})

  const source = fs.createReadStream('big_img.jpg')
  // using writeStream here, the final code will do s3 upload instead
  const pngTarget = fs.createWriteStream('resized.png')
  const jpgTarget = fs.createWriteStream('resized.jpg')

  const promises = [
    pipeline(readableStream, sharpStream), // don't do this piping twice!
    pipeline(sharpStream.clone().png(), memoryPng),
    pipeline(sharpStream.clone().jpeg(), memoryJpg),
  ]
  return Promise.all(promises)
}

console.time('resize')
resizeJob().catch(err => {
  console.log(err)
}).then(() => {
  console.timeEnd('resize')
})

谢谢现在我明白了为什么最初的promise版本返回得如此之快,管道方法没有返回一个承诺:)你的解决方案工作得很好,我只是适应了返回MemoryStream,因为我必须将它们传递到另一个模块,并且不使用fs,因为它是一个express应用程序