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

时间:2017-07-01 02:49:37

标签: javascript node.js mongodb gridfs

我知道循环何时完成,

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

1 个答案:

答案 0 :(得分:3)

可能最好在这里使用default style,但是您需要将每个“文件”包装在Promise中并根据每个文件中的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
  });
});

这不涉及其他依赖项,但您可以使用Promise.all作为附加依赖项:

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

所以他们看起来非常相似,基本上他们也在做同样的事情。在每种情况下,“循环”现在是async.map,它将当前文件名作为参数传递,并返回已转换响应的数组,在本例中是base64函数的输出。它们的关键在于resolvecallback基本上可以控制事情的发生。

在使用.map()的情况下,Promise.all是数组上的基本JavaScript .map()函数,它实际上返回了一个“.map()数组”,它们都实现了rejectresolve函数,然后由流上的相应处理程序调用。

这里的“Promises”全部在Promise内执行并返回结果,并将输出数组传递给具有.then()的块,该块具有内容,然后可以传递给您的方法以更新/可能是这样的。

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

实际执行调用之间存在细微差别,但两者基本上都应用了我们“信号”输出完成的相同原则到提供“循环”的机制,以便我们知道“所有”何时完成。