Помощник официанта завершает работу перед анализом всех данных

#node.js #express #busboy

#node.js #экспресс #помощник официанта

Вопрос:

У меня есть экспресс-приложение и есть функция, которая использует busboy для анализа данных формы, которая возвращает значения полей формы, но не анализирует все поля перед обратным вызовом.

 
module.exports = async function (headers, invalidMime, res ,req) {
    let fields = {};
    const fileWrites = []
    let filesToUpload = [];
    let finished = false;
    const busboy = new Busboy({
        headers: headers,
        limits: { ileSize: 10 * 1024 * 1024 }
    });

    await busboy.on("field", (fieldname, val) => {
        console.log(fieldname); // Log 1
        fields[fieldname] = val;
    });

    await busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
        
        if (invalidMime(mimetype)) return res.status(404).json({ [fieldname]: "Wrong file type submitted" });
        
        file.on("limit", () => { return res.status(404).json({ general: "File size too large!" }); });

        const randomizedFileName = createFileName(filename);
        const filePath = path.join(os.tmpdir(), randomizedFileName);

        fileWrites.push(createImagePromise(file, filePath, randomizedFileName, mimetype, fieldname));
    });

    busboy.on("finish", () => {
      console.log(fields); // Log 2
    });
      
    busboy.end(req.rawBody);

    return {filesToUpload: fileWrites, fields: fields}
}
 

Это возвращает мои поля, но не хватает последнего. Когда я отлаживаю его с помощью журнала консоли, я вижу, что busboy.on("finish") он выполняется после того, как я возвращаю свои значения, что приводит к отсутствию переменных.

 const formData = await  FormParser(req.headers, invalidMime, res, req);

console.log(formData); // Log 3
const { fields, filesToUpload } = formData;

 
   var1                     // Log 1 - Before return
  var2                     // Log 1 - Before return
  var3                     // log 1 - Before return
  { filesToUpload: [],     // Log 3 - After return
    fields:
     { var1: 'var1',
       var2: 'var2',
       var3: 'var3' } }          
  var4                     // Log 1 - After return
  { var1: 'var1',          // Log 2 - After return
    var2: 'var2',
    var3: 'var3',
    var4: 'var4' }                

 

Как я могу заставить busboy возвращать значения после завершения анализа?

Комментарии:

1. что такое busboy.on ?

2. Это из-за асинхронности / ожидания. Ваш «финиш» выполняется после того, как обещание уже выполнено.

3. @MohammadYaserAhmadi он используется для анализа значений полей из данных формы.

4. @RichardRublev когда я удаляю синтаксис async await, результат тот же. Как я могу обойти это поведение?

5. можете ли вы показать .on функцию, которая как реализована?

Ответ №1:

Асинхронные функции волшебным образом не становятся синхронными, когда вы добавляете несколько await секунд то тут, то там.

Например, это не имеет смысла:

 await busboy.on("field", (fieldname, val) => {
    console.log(fieldname); // Log 1
    fields[fieldname] = val;
});
 

Вы думаете, что он каким-то образом ожидает fields заполнения объекта, но на самом деле он делает следующее: он ожидает возврата .on() функции, и эта функция возвращается немедленно. Его единственная задача — назначить обработчик событий. Это field событие еще даже не произошло вообще.

Решение в асинхронном программировании всегда таково: выполните работу в обработчике событий, которая означает, что задача завершена. Вы пытаетесь выполнить работу ( return {filesToUpload: fileWrites, fields: fields} ) в последней строке вашей функции, как если бы последняя строка была последней для запуска. Это не так.

Как только вы переместите биты, которые должны реагировать на события внутри обработчиков событий, вы обнаружите, что целая функция вообще не нужна async .

Отказ от ответственности: следующий код не проверен, я раньше не использовал busboy, делайте то, что это значит, не обязательно то, что он говорит.

 module.exports = function (headers, invalidMime, res, req) {
    let fields = {};
    let pendingFileWrites = [];

    const busboy = new Busboy({
        headers: headers,
        limits: { fileSize: 10 * 1024 * 1024 }
    });

    busboy.on("filesLimit", () => {
        res.status(400).json({ error: "File size too large!" });
    });
    busboy.on("error", () => {
        res.status(500).json({ error: "Error parsing data" });
    });
    busboy.on("field", (fieldname, val) => {
        fields[fieldname] = val;
    });
    busboy.on("finish", () => {
        Promise.all(pendingFileWrites).then((fileWrites) => {
            // NOW we're done
            res.json({
                filesToUpload: fileWrites, 
                fields: fields
            });
        });
    });

    busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
        console.log(`Processing [{filename}] ({mimetype})`);
        if (invalidMime(mimetype)) return res.status(404).json({ [fieldname]: "Wrong file type submitted" });
        file.on("limit", () => { return res.status(404).json({ general: "File size too large!" }); });
        file.on("end", () => { console.log(`Done processing [{filename}] ({mimetype})`); });

        const randomizedFileName = createFileName(filename);
        const filePath = path.join(os.tmpdir(), randomizedFileName);

        pendingFileWrites.push(createImagePromise(file, filePath, randomizedFileName, mimetype, fieldname));
    });
};
 

Обратите внимание, что вы могли бы сделать это async :

 busboy.on("finish", () => {
    Promise.all(pendingFileWrites).then((fileWrites) => {
        // NOW we're done
        res.json({
            filesToUpload: fileWrites, 
            fields: fields
        });
    });
});
 

Нравится

 busboy.on("finish", async () => {
    var fileWrites = await Promise.all(pendingFileWrites);
    // NOW we're done
    res.json({
        filesToUpload: fileWrites, 
        fields: fields
    });
});
 

или даже

 busboy.on("finish", async () => {
    res.json({
        filesToUpload: await Promise.all(pendingFileWrites), 
        fields: fields
    });
});
 

если бы вы захотели. В любом случае вам нужно добавить обработку ошибок (первый через .catch() , последний через try / catch block).