Promise.all()在完全填充promise数组之前触发

时间:2017-01-29 20:59:39

标签: javascript node.js ecmascript-6 promise

我正在尝试实现多个异步调用,并且仅在完成所有操作后才继续。

过程是这样的,我需要按此顺序完成: 获取文件路径列表, 将它们复制到新目的地, 转换位图, 创建缩略图, 将信息添加到Array中以批量插入DB

为此,我使用了promises数组和Promise.all()方法。此方法过早触发,因为当promise阵列尚未(完全)填充时会执行此方法。 如果我在for循环之后延迟一段时间它可以工作,但所需的时间取决于文件的数量。

这是主要的电话:

queueFiles(files).then(function () {
    // Add to DB
    console.log(current_import.sql.length); // Number of all datasets (0 at runtime)
    console.log(current_import.sql);        // An array of datasets   ([] at runtime)
});

这是不能按预期工作的功能:

let import_promises = {
    copy_promises : [],
    convert_promises : [],
    thumbnail_promises : [],
    sql_promises : []
};

function queueFiles(files) {
    return new Promise(function (resolve, reject) {
        try {
            for (file of files) {
                let original_filename = file.split("\\").pop();
                let original_extension = original_filename.split(".").pop();

                let hashed_name = crypto.createHmac('sha256', secret).update(original_filename).digest('hex') + "." + original_extension;
                let new_path = data_folder + "/" + hashed_name;

                import_promises.copy_promises.push(fs.copy(file, new_path).then(function () {
                    if (original_extension == "bmp") {
                        import_promises.convert_promises.push(convertFile(new_path).then(function (result) {
                            import_promises.thumbnail_promises.push(createThumbnail(result.path, thumb_folder + "/" + result.path.split("/").pop(), 500).then(function () {
                                import_promises.sql_promises.push(addSql(result.data));
                            }));
                        }));
                    } else {
                        import_promises.thumbnail_promises.push(createThumbnail(new_path, thumb_folder + "/" + hashed_name, 500).then(function () {
                            import_promises.sql_promises.push(addSql(new_path));
                        }));
                    }
                }));
            }

            Promise.all([import_promises.copy_promises, import_promises.convert_promises, import_promises.thumbnail_promises, import_promises.sql_promises])
                .then(() => {
                    // All files copied, converted, thumbnails created and prepared to insert into DB
                    resolve();
                });
        }
        catch (err) {
            reject(err);
        }
    });
}

这些是辅助功能:

function createThumbnail(input, output, width) {
    return new Promise(function (resolve, reject) {
        try {
            gm(input)
                .scale(width)
                .write(output, function (err) {
                    if (err) {
                        throw(err);
                    }
                    resolve();
                });
        }
        catch (err) {
            reject(err);
        }
    });
}

function convertFile(newPath) {
    return new Promise(function (resolve, reject) {
        try {
            let convertedPath = newPath.replace(/\.[^/.]+$/, "") + ".png";

            execFile("convert", [newPath, convertedPath]).then(function () {
                fs.unlinkSync(newPath);
                resolve({path: convertedPath});
            });
        }
        catch (err) {
            reject(err);
        }
    });
}

function addSql(data) {
    return new Promise(function (resolve, reject) {
        try {
            current_import.sql.push(data);

            current_import.done++;
            updateProgressBar();

            resolve();
        }
        catch (err) {
            reject(err);
        }
    });
}

已编辑的代码(工作):

function createThumbnail(input, output, width) {
    return new Promise(function (resolve, reject) {
        gm(input)
            .scale(width)
            .write(output, function (err) {
                if (err) {
                    return reject(err);
                }
                resolve();
            });
    });
}

function convertFile(newPath) {
    let convertedPath = newPath.replace(/\.[^/.]+$/, "") + ".png";

    return execFile("convert", [newPath, convertedPath]).then(function () {
        fs.unlinkSync(newPath);
        return convertedPath;
    });
}

function addSql(data) {
    current_import.sql.push(data);

    current_import.done++;
    updateProgressBar();
}

function queueFiles(files) {
    return Promise.all(files.map(function (file) {
        let original_filename = file.split("\\").pop();
        let original_extension = original_filename.split(".").pop();

        let hashed_name = crypto.createHmac('sha256', secret).update(original_filename).digest('hex') + "." + original_extension;
        let new_path = data_folder + "/" + hashed_name;

        return fs.copy(file, new_path).then(function () {
            if (original_extension == "bmp") {
                return convertFile(new_path)
                    .then(function (path) {
                        return {path: path, hash: path.split("/").pop()};
                    });
            } else {
                return {path: new_path, hash: hashed_name}
            }
        }).then(function (result) {
            let outPath = thumb_folder + "/" + result.hash;
            return createThumbnail(result.path, outPath, 500)
                .then(function () {
                    return outPath;
                });
        }).then(function (thumb_path) {
            return addSql(thumb_path);
        });
    }));
}

1 个答案:

答案 0 :(得分:0)

首先,convertFilesqueueFiles不需要Promise构造函数

但问题是...... import_promises.copy_promises是一系列承诺,import_promises.sql_promises是一系列承诺,而import_promises.convert_promises是一系列承诺......所以,你是在Promise数组的数组上执行Promise.all ... Promise.all期望一个承诺数组

chnage

Promise.all([import_promises.copy_promises, import_promises.convert_promises, import_promises.thumbnail_promises, import_promises.sql_promises]).then ...

Promise.all([].concat(import_promises.copy_promises, import_promises.convert_promises, import_promises.thumbnail_promises, import_promises.sql_promises).then ...

至于anipatterns,converFiles可以简单地

function convertFile(newPath) {
    let convertedPath = newPath.replace(/\.[^/.]+$/, "") + ".png";

    return execFile("convert", [newPath, convertedPath]).then(function () {
        fs.unlinkSync(newPath);
        resolve({path: convertedPath});
    });
}

addsql甚至不是异步的,所以为什么它会返回一个承诺是一个谜 -

和queueFiles(没有深入研究那里的其他一些问题)可以

function queueFiles(files) {
    for (file of files) {
        let original_filename = file.split("\\").pop();
        let original_extension = original_filename.split(".").pop();

        let hashed_name = crypto.createHmac('sha256', secret).update(original_filename).digest('hex') + "." + original_extension;
        let new_path = data_folder + "/" + hashed_name;

        import_promises.copy_promises.push(fs.copy(file, new_path).then(function () {
            if (original_extension == "bmp") {
                import_promises.convert_promises.push(convertFile(new_path).then(function (result) {
                    import_promises.thumbnail_promises.push(createThumbnail(result.path, thumb_folder + "/" + result.path.split("/").pop(), 500).then(function () {
                        import_promises.sql_promises.push(addSql(result.data));
                    }));
                }));
            } else {
                import_promises.thumbnail_promises.push(createThumbnail(new_path, thumb_folder + "/" + hashed_name, 500).then(function () {
                    import_promises.sql_promises.push(addSql(new_path));
                }));
            }
        }));
    }

    return Promise.all([import_promises.copy_promises, import_promises.convert_promises, import_promises.thumbnail_promises, import_promises.sql_promises]);
}
  在尝试理解流程之后,我已经想出了这个,我唯一不理解的部分是addSql函数,为什么它是一个承诺,

function convertFile(newPath) {
    return execFile("convert", [newPath, convertedPath]).then(function () {
        fs.unlinkSync(newPath);
        return convertedPath;
    });
}

function createThumbnail(input, output, width) {
    return new Promise(function(resolve, reject) {
        gm(input)
        .scale(width)
        .write(output, function (err) {
            if (err) {
                return reject(err);
            }
            resolve();
        });
    });
}

function addSql(data) {
    current_import.sql.push(data);

    current_import.done++;
    updateProgressBar();
}

function queueFiles(files) {
    return Promise.all(files.map(function(file) {
        return fs.copy(file, new_path).then(function () {
            if (original_extension == "bmp") {
                return convertFile(new_path)
                .then(function(path) {
                    return {path: path, hash: path.split("/").pop()};
                });
            } else {
                return {path: new_path, hash: hashed_name}
            }
        }).then(function (result) {
            return createThumbnail(result.path, thumb_folder + "/" + result.hash, 500)
            .then(function() {
                return result.path;
            });
        }).then(function(result) {
            return addSql(result);
        });

    }));
}