使用 Javascript(以及 Node.js)使用 async/await 和 Promises 的正确方法是啥 [重复]
Posted
技术标签:
【中文标题】使用 Javascript(以及 Node.js)使用 async/await 和 Promises 的正确方法是啥 [重复]【英文标题】:What's the correct way to use async/await and Promises with Javascript (and therefore Node.js) [duplicate]使用 Javascript(以及 Node.js)使用 async/await 和 Promises 的正确方法是什么 [重复] 【发布时间】:2021-11-15 19:06:14 【问题描述】:我实际上正在构建一个脚本来从 mysql 数据库中提取数据,然后填充 MongoDB。在这个过程中,有一些异步的东西,比如建立与 MySQL(通过 Sequelize 库)和 MongoDB(通过 Mongoose 库)的连接,还有一些同步的东西,比如获取和转换数据。
我阅读了很多关于 async/await 和 Promises 的内容,我的脚本在全球范围内执行我想要的操作,但仍然存在一些问题。
代码如下:
Migration.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 => contact@example.com
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":"contact@example.com","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
,但是转换过程还没有完成,尽管有等待语句并且它返回了一个Promise。
之后我们还可以看到BillingInfos: id_customer => 1
和BillingInfo: company => ExampleCompany
表示转换过程仍在循环中做事。
然后F - Connection to MongoDB
然后另一个转换记录 BillingInfos: lastname => Doe
和 BillingInfo: firstname => John
(转换过程仍在循环中进行)。
然后G - Insert MongoDB data.
最后是验证错误,因为某些 Mongo 文档不完整,因此规则未完整归档。
问题?
所以问题是我在这里做错了什么?
正如我所说,我阅读了很多关于 async/await 和 Promises 的内容,但仍然难以理解为什么它不起作用。
提前致谢,如果您需要更多信息,请告诉我。
【问题讨论】:
【参考方案1】:那是因为 await will not work inside 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);
【讨论】:
是的,它有效!我现在明白我的错误了。对我来说,引擎盖下发生了什么变得越来越清楚。此外,我应该阅读更多关于“forEach”文档,尤其是蓝色“Notes”的信息:MDN Reference我想从现在开始我肯定会禁止“forEach”并且只使用它的非异步替代品,如for
、@987654328 @ 或 for ... of
按照您的建议。非常感谢!以上是关于使用 Javascript(以及 Node.js)使用 async/await 和 Promises 的正确方法是啥 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
72.vue开发工具node.js以及构建工具webpack