在 Javascript(以及 Node.js)中使用 async/await 和 Promises 的正确方法是什么?

扭蛋

我实际上正在构建一个脚本来从 MySQL 数据库中提取数据,然后填充 MongoDB。在这个过程中,有一些异步的东西,比如建立到 MySQL(通过 Sequelize 库)和 MongoDB(通过 Mongoose 库)的连接,还有一些同步的东西,比如获取和转换数据。

我阅读了很多关于 async/await 和 Promises 的内容,我的脚本正在全局执行我想要的操作,但仍然存在一些问题。


这是代码:


迁移.class.mjs

import MigrationBase from './Base/MigrationBase.class.mjs';

export default class Migration extends MigrationBase
{
    constructor(config) {
        super(config);

        this.mysqlData = {};
        this.mongoData = {};
    }

    async run() {
        await this.selectMySQLData();
        let docs = await this.convertMySQLToMongo();
        await this.checkConvertedData(docs);
        await this.insertMongoData();
    }

    async selectMySQLData() {
        return new Promise(async resolve => {
            await this.runSequelize();
            console.log('B - Grabbing MySQL data\n');

            for(var key in this.mysqlModels) {
                if (this.mysqlModels.hasOwnProperty(key)) {
                    let search = { raw: true };
                    this.mysqlData[key] = await this.mysqlModels[key].findAll(search);
                }
            }

            await this.closeSequelize();
            resolve();
        });
    };

    convertMySQLToMongo() {
        return new Promise(async resolve => {
            console.log('D - Convert MySQL data to MongoDB\n');

            let customersDocument = this.defaultDocuments.customers;
            let personalInfosDocument = this.defaultDocuments.personal_infos;
            let billingInfosDocument = this.defaultDocuments.billing_infos;
            // ... etc ...

            await Object.entries(this.mysqlData.customer).forEach(async keyRow => {
                let [key, row] = keyRow;

                await Object.entries(row).forEach(async keyValue => {
                    customersDocument = await this._processCustomersFields(customersDocument, 'Customer', keyValue);
                    personalInfosDocument = await this._processPersonalInfosFields(personalInfosDocument, 'PersonalInfo', keyValue);
                    billingInfosDocument = await this._processBillingInfosFields(billingInfosDocument, 'BillingInfo', keyValue);
                    // ... etc ...
                    
            });

            resolve([
                customersDocument,
                personalInfosDocument,
                billingInfosDocument,
                // ... etc ...
            ]);
        });
    };

    checkConvertedData([
        customersDocument,
        personalInfosDocument,
        billingInfosDocument,
        // ... etc ...
    ]) {
        return new Promise(resolve => {
            console.log('E - Checking converted data');

            if (! this._isNull(customersDocument, 'Customers')) {
                this.mongoData.customers = customersDocument;
            }
            
            if (! this._isNull(personalInfosDocument, 'PersonalInfos')) {
                this.mongoData.personal_infos = personalInfosDocument;
            }
            
            if (! this._isNull(billingInfosDocument, 'BillingInfos')) {
            
            }   this.mongoData.billing_infos = billingInfosDocument;
            // ... etc ...
            
            resolve();
        });
    }

    async insertMongoData() {
        return new Promise(async resolve => {
            await this.runMongoose();
            console.log('G - Insert MongoDB data.');
            
            await this.mongoModels.customers.create(this.mongoData.customers);
            await this.mongoModels.personal_infos.create(this.mongoData.personal_infos);
            await this.mongoModels.billing_infos.create(this.mongoData.billing_infos);
            // ... etc ...

            await this.closeMongoose();
            resolve();
        });
    };

    _processCustomersFields(defaultDoc, docName, [colName, val]) {
        return new Promise(resolve => {
            switch (colName) {
                case 'id_customer':
                    console.log(`${docName}: ${colName} => ${val}`);
                    defaultDoc.id = val;
                    break;
                case 'email_customer':
                    console.log(`${docName}: ${colName} => ${val}`);
                    defaultDoc.email = val;
                    break;
                case 'password_customer':
                    console.log(`${docName}: ${colName} => ${val}`);
                    defaultDoc.password = val;
                    break;
                // ... etc ...
            }
    
            resolve(defaultDoc);
        });
    }

    _processPersonalInfosFields(defaultDoc, docName, [colName, val]) {
        return new Promise(resolve => {
            switch (colName) {
                // ... Same kind of code as in _processCustomersFields() ...
            }
            
            resolve(defaultDoc);
        });
    }

    _processBillingInfosFields(defaultDoc, docName, [colName, val]) {
        return new Promise(resolve => {
            switch (colName) {
                // ... Same kind of code as in _processCustomersFields() ...
            }
            
            resolve(defaultDoc);
        });
    }
    
    _isNull(document, mongoName) {
        if (document !== null) {
            console.log(`\n${mongoName}:\n`, JSON.stringify(document));
            return false;
        } else {
            console.log(`Error processing \`${mongoName}\` data!`);
            return true;
        }
    }
    
    _valueExists(val) {
        return (val !== null && val !== "" && typeof val !== "undefined")
            ? true
            : false
        ;
    }
}

MigrationBase.class.mjs

import Sequelize from 'sequelize';
import DataTypes from 'sequelize';
import Mongoose from 'mongoose';
import Crypto from 'crypto';
import Models from '../../../models.mjs';
import Schemas from '../../../schemas.mjs';

export default class MigrationBase
{
    constructor(config) {
        this.config = config;
        this.sequelize = this.createSequelize();
        this.mongoose = Mongoose;
        this.defaultDocuments = this.initDefaultDocuments();
        this.mysqlModels = this.initMysqlModels();
        this.mongoModels = this.initMongoSchemas();
        this.mysqlData = {};
        this.mongoData = {};
    }

    createSequelize() {
        return new Sequelize(
            this.config.mysql.dbName,
            this.config.mysql.dbUser,
            this.config.mysql.dbPass,
            this.config.sequelize
        );
    }

    initDefaultDocuments() {
        const defaultDocument = {
            "deleted_at": 0 // Thu Jan 01 1970 01:00:00 GMT+0100
        };

        let defaultDocuments = {
            "customers": Object.assign({}, defaultDocument),
            "personal_infos": Object.assign({}, defaultDocument),
            "billing_infos": Object.assign({}, defaultDocument)
            // ... etc ...
        };

        return defaultDocuments;
    }

    initMysqlModels() {
        return {
            "customer": Models.Customer(this.sequelize, DataTypes),
            "billing_address": Models.BillingAddress(this.sequelize, DataTypes),
            // ... etc ...
        };
    }

    initMongoSchemas() {
        return {
            "customers": this.mongoose.model('Customer', Schemas.Customers),
            "personal_infos": this.mongoose.model('PersonalInfo', Schemas.PersonalInfos),
            "billing_infos": this.mongoose.model('BillingInfo', Schemas.BillingInfos),
            // ... etc ...
        }
    }

    async runSequelize() {
        console.log('A - Connection to MySQL');

        try {
            await this.sequelize.authenticate();
            console.log('Connection to MySQL has been established successfully.\n');
        } catch (err) {
            console.error("Unable to connect to the MySQL database:", err + '\n');
        }
    }

    async closeSequelize() {
        console.log('C - Closing MySQL connection.\n');

        await this.sequelize.close();
    };

    runMongoose() {
        return new Promise(async resolve => {
            console.log('F - Connection to MongoDB');

            try {
                await this.mongoose.connect(
                    `mongodb://${this.config.mongo.dbHost}:${this.config.mongo.dbPort}/${this.config.mongo.dbName}`,
                    { useNewUrlParser: true, useUnifiedTopology: true }
                );

                console.log('Connection to MongoDB has been established successfully.');
            } catch (err) {
                console.error('Unable to connect to the MongoDB database: ', err);
            }

            resolve();
        });
    }

    async closeMongoose() {
        console.log('H - Closing MongoDB connection.');
        await this.mongoose.connection.close();
    };
}

这是日志输出:

A - Connection to MySQL
Connection to MySQL has been established successfully.

B - Grabbing MySQL data

C - Closing MySQL connection.

D - Convert MySQL data to MongoDB

Customer: id_customer => 1
Customer: email_customer => [email protected]
Customer: password_customer => 0a1b2c3d4e5f0a1b2c3d4e5f0a1b2c3d
// ... etc ...
PersonalInfo: id_customer => 1
PersonalInfo: lastname_customer => Doe
PersonalInfo: firstname_customer => John
// ... etc ...
E - Checking converted data

Customers:
 {"deleted_at":0,"id":"000000000000000000000001","email":"[email protected]","password":"0a1b2c3d4e5f0a1b2c3d4e5f0a1b2c3d", ... etc ... }

PersonalInfos:
 {"deleted_at":0,"customer_id":"000000000000000000000001","last_name":"Doe","first_name":"John", ... etc ... }

BillingInfos:
 {"deleted_at":0}
BillingInfos: id_customer => 1
BillingInfo: company => ExampleCompany
F - Connection to MongoDB
BillingInfos: lastname => Doe
BillingInfo: firstname => John
Connection to MongoDB has been established successfully.
G - Insert MongoDB data.
/home/user/Workspaces/namespace/project-name/node_modules/mongoose/lib/document.js:2757
    this.$__.validationError = new ValidationError(this);
                               ^

ValidationError: BillingInfos validation failed: id_customer: Cast to ObjectId failed for value "1" (type number) at path "customer_id", values: Path `values` is required., id: Path `id` is required.

在这里我们可以看到正确的顺序:

A - Connection to MySQL

B - Grabbing MySQL data

C - Closing MySQL connection

D - Convert MySQL data to MongoDB

然后我们可以看到E - Checking converted data,尽管有 await 语句并且它返回了一个 Promise,但转换过程并没有完成。

之后我们还可以看到BillingInfos: id_customer => 1BillingInfo: company => ExampleCompany表示转换过程仍在循环中执行操作。

然后 F - Connection to MongoDB

然后另一个转换记录BillingInfos: lastname => DoeBillingInfo: firstname => John(转换过程仍在循环中执行操作)。

然后 G - Insert MongoDB data.

最后是验证错误,因为某些 Mongo 文档不完整,因此规则不完整。


题?

所以问题是我在这里做错了什么?

正如我所说,我阅读了很多关于 async/await 和 Promises 的内容,但仍然很难理解为什么它不起作用。

提前致谢,如果您需要更多信息,请告诉我。

弗里特

那是因为await 在 forEach() 中不起作用,您正在尝试在convertMySQLToMongo()函数中执行此操作。

有很多方法可以解决它,其中一种方法是使用for ... of代替forEach()

for (const keyRow of Object.entries(this.mysqlData.customer)) {
    let [key, row] = keyRow;
  
    for (const keyValue of Object.entries(row)) {
        customersDocument = await this._processCustomersFields(customersDocument, 'Customer', keyValue);
        personalInfosDocument = await this._processPersonalInfosFields(personalInfosDocument, 'PersonalInfo', keyValue);
        billingInfosDocument = await this._processBillingInfosFields(billingInfosDocument, 'BillingInfo', keyValue);
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

设置 Node.js 和 MySQL 的正确方法是什么?

使用 Promise.reject 和 javascript 的正确方法是什么

Node 6.14.0 asyncawait npm 包和谷歌云函数 -> Javascript 异步函数

如何在 Node.js 中使用“async/await”和“promises”进行同步执行?

将 docker-compose 与 mongodb、node 和 postman 一起使用的正确方法是什么?

从Node使用Jasmine的正确方法是什么?

使用Node.js和Promises实现指数补偿

以下哪个代码是在JavaScript中使用Promises的正确方法?

在Angular中使用`useFactory`和`deps:[]`的正确方法是什么?

在Java中使用日期和日历的正确方法是什么?

在 appscript 中使用 OR 和 AND 的正确方法是什么?

在node.js中使用async / await处理承诺拒绝的正确方法是什么?

使用node.js postgresql模块的正确方法是什么?

使用Node.js MySQL库的正确方法是什么?

如何在 Javascript 中使用异步和等待的 Promises

在Node.js中使用async-await和promises时无法按预期顺序获得结果

使用CSS和Javascript向HTML DOM对象添加动画的正确方法是什么?

在 webpacker 中使用 in-HTML javascript 的正确方法是什么?

在Node JS中使用Promises制作字符串

在 Node 和 AWS Lambda 中使用 Async & Promises 通过 FTP 下载 txt 文件

什么是使用await代替Promises的正确方法?

Node.js:拆分代码的正确方法是什么?

Node.js中要求的正确方法是什么?

在 node.js 中 fork 循环的正确方法是什么

Javascript、Promises 和 setTimeout

在Node.js中使用Akka的最佳方法是什么

在Visual Studio代码中使用TypeScript和Webpack调试Node.js项目的正确方法

使用骆驼和activemq“暂停”路线的正确方法是什么?

使用PARENT和FIND的正确方法是什么?