如何在保存之前等待创建流的循环完成?

How to wait for a loop creating streams to finish before saving?

我不知道循环何时结束,

app.post('/api/books', upload.array('images'), function(req, res) {
    let book = req.body.book;
    let arr = [];

    // GridFS get connection with DB   
    var gfs = gridfsstream(conn.db);
    var writestream;

    for (i = 0; i < req.files.length; i++) {
        writestream = gfs.createWriteStream({
            filename: req.files[i].originalname
        });
        fs.createReadStream('uploads/' + req.files[i].filename).pipe(writestream);
        writestream.on("close", function(file) {
            console.log(file.filename + "stored successfully into mongodb using gridfs");
        });
        writestream.on("error", function(file) {
            console.log(file.filename + "not stored into mongodb using gridfs");
        });

        base64(writestream.name, function(response) {
            arr.push(response);
        });
    }

    book.images = arr;
    Book.addBook(book, function(err, book) {
        if (err) {
            throw err;
        }
        res.json(book);
    });
});

问题是:我做的时候数组arr是空的

book.images = arr 

我需要等待 for 循环完成,但我该怎么做?

我知道这行得通,因为我已经放了一个 console.log() 并且可以正常工作

base64(writestream.name, function(response) {
    arr.push(response);
});

可能最好在此处使用 Promise.all,但是您需要将每个 "files" 包装在一个 Promise 中,并且 return 根据每个 writeStream 的时间进行解析已完成或错误:

app.post('/api/books', upload.array('images'), function(req,res) {
  let book = req.body.book;
  var gfs = gridfsstream(conn.db);

  Promise.all(
    req.files.map(function(file) => {
      return new Promise(function(resolve,reject) {
        var writestream = gfs.createWriteStream({
          filename: file.originalname
        });

        fs.createReadStream('uploads/'+file.filename).pipe(writestream);

        writestream.on("error",reject);
        writestream.on("close",function() {
           base64(writestream.name, function(response) {
             resolve(response);
           });
        });

      })
    })
  )
  .then(function(images) {
    book.images = images;
    Book.addBook(book,function(err,book) {
      if (err) throw err; // or whatever
      res.json(book)
    });
  })
  .catch(function(err) => {
    // Deal with errors
  });
});

这不涉及额外的依赖项,但是您可以交替使用 async.map 作为额外的依赖项:

app.post('/api/books', upload.array('images'), function(req,res) {
  let book = req.body.book;
  var gfs = gridfsstream(conn.db);

   async.map(
     req.files,
     function(file,callback) {
        var writestream = gfs.createWriteStream({
          filename: file.originalname
        });

        fs.createReadStream('uploads/'+file.filename).pipe(writestream);

        writestream.on("error",callback);
        writestream.on("close",function() {
           base64(writestream.name, function(response) {
             callback(null,response);
           });
        });                      
     },
     function(err,images) {
       if (err) throw err;

       book.images = images;
       Book.addBook(book,function(err,book) {
         if (err) throw err; // or whatever
         res.json(book)
       });
     }
   );
});

所以他们看起来很相似,基本上他们在做同样的事情。在每种情况下,"loop" 现在是一个 .map(),它传入当前文件名作为参数,return 是一个转换后的响应数组,在本例中是 base64函数。他们的关键是 resolvecallback 基本上控制着事情发生的时间。

在使用 Promise.all 的情况下,.map() 是数组上的基本 JavaScript .map() 函数,本质上是 returning 一个 "array of Promise" 都实现了 rejectresolve 函数,然后由流中的相应处理程序调用。

此处 "Promises" 全部执行,return 在 Promise.all 中产生结果,并将输出数组传递给 .then() 块,其中包含内容并且可以然后根据情况将您的方法传递给 update/create。

async.map 示例中,它使用了一个 callback 参数,该参数再次提供给流上的事件处理程序。以完全相同的方式,最后一个块接收输出或错误,并可以再次传递给您的方法以保留数据。

调用的实际执行略有不同,但本质上都是将我们"signal"输出完成的原理应用到提供"loop"的机制上,这样我们就知道什么时候"all" 已完成。