Async/Await 在 NodeJS 中
Async/Await In NodeJS
这在 NodeJS 中有效,但我必须在创建它的代码块中使用 recordObject。
即使在函数的开头声明了 recordObject,它在代码块之外也是不可见的,因为函数在检索到 recordObject 之前继续执行。我想使用 async/await 执行此操作,但我不知道从 helpers.getMostRecent.
返回后从哪里获取 recordObject
这是整个函数。
有人可以告诉我如何使用 Async/Await 来做到这一点吗?
谢谢,约翰
// Define a function which builds a webpage and all supporting client/server code for
// adding records to a json table in the database.
meta.build.AddWebpage = function(tableId)
{
let dataObject = {};
dataObject.uniqueField01Value = "";
dataObject.uniqueField01Name = "table.tableName";
dataObject.path = '/dbMetadata/metadata.json';
dataObject.queryString = 'WHERE:;tableId:;MatchesExactly:;' + tableId + ':;';
// Collect information about the webpage from the metadata.
// 1. Look in metadata.json - Read the object for the given tableId.
helpers.getMostRecent(dataObject, function(errorFromGetMostRecent, payload)
{
if(!errorFromGetMostRecent) // Got the most recent record from getMostRecent
{
// Used to decode the payload buffer into readable text.
let decoder = new StringDecoder('utf8');
// This instance of the Writable object gives us a place for a callback to run when the payload is received.
const writable = new Writable();
// Called by pipeline below. Does something useful with the payload
writable.write = function(payload)
{
let stringContainer = '';
stringContainer = stringContainer + decoder.write(payload);
let recordObject = JSON.parse(stringContainer);
// recordObject is all the metadata for the table.
// I can use recordObject to build the webpage and all supporting code for adding records to a table
///////////////My problem is I have to do everything else in this code block////////////////////
?????????????????????????????How can I change this to Async/Await???????????????????????????????
}; // End of: writable.write = function(payload){...}
// Passes the payload stream to the writable object which calls writable.write
// which does something useful with the payload.
pipeline
(
payload,
writable,
function(error){if(error){console.log('There was an error.');}}
);
} // End of: if(!error) Got the most recent record from gitHashedPass
else // There was indeed an error returned by getMostRecent when attempting to get the most current record.
{
helpers.log // Log the error.
(
7,
'bxpa2p2t7ps3wrd1dqu7' + '\n' +
'The following was the error message from getMostRecent:' + '\n' +
errorFromGetMostRecent + '\n'
); // End of: helpers.log // Log the error.
} // End of: Else // There was indeed an error returned by getHashedPass when attempting to get the most current record.
}); //End of: helpers.getMostRecent(dataObject, function(errorFromGetMostRecent, payload)
// Assemble the webpage string from the metadata in recordObject
}// End of: meta.build.AddWebpage = function(tableId){...}
这里是函数 getMostRecent,它被上面的函数调用以获取关于我们希望添加到会计系统的 table 的 json 记录。
// Define a function to retrieve the most current record in a table for a given primary key.
// Serves as table validation for put handlers (editing existing records)
// Checks that a record with the supplied primary key exists to modify.
// Also checks that a candidate values supplied for a unique fields are not already in the table. Enforces uniqueness.
// Streams the most current record back to the calling function.
helpers.getMostRecent = function(data, callback)
{
// No value set but declared here because we need access to these thoughout the entire function.
let queryArray, queryString;
// typical example of a queryString: 'WHERE:;userId:;MatchesExactly:;' + userId + ':;'
queryString = data.queryString
// Make an array out of the queryString where each phrase of the query is an element.
queryArray = queryString.split(":;");
// Create an empty map data structure which will be used to merge records with the same primary key.
// Hard if not impossible do that with objects.
let tableMap = new Map();
// Create a variable to track whether or not candidate values for any unique fields have already been used.
let uniqueValuesAlreadyUsed = false;
// Create a variable to track the primary key of a record that we may encounter which
// is holding a candidate unique value (perhaps and email address or phone number).
// If we encounter this primary key again as we proceed through the records then we will
// check to see if the candidate unique value has been changed or if the record has been deleted.
// If so we will set this variable to false signifying that the candidate unique value is available again.
let primaryKeyOfRecordHoldingCandidateUniqueValue = false
// This function sets up a stream where each chunk of data is a complete line in the table file.
let readInterface = readline.createInterface
(
{ // specify the file to be read.
input: fs.createReadStream(helpers.baseDir + data.path)
}
);
// Look at each record in the file.
readInterface.on('line', function(line)
{
// Convert the JSON string (a single line from the table file) into lineValueObject.
let lineValueObject = JSON.parse(line);
// Declare a variable to serve as a key in the map to manage the lineValueObject.
let primaryKey = lineValueObject[queryArray[1]];
let shouldDeleteThisRecord = false;
if(lineValueObject.deleted === true) // if the record in the table file had the delete field set to true:
{
// If this record was holding our candidate unique field value:
if(lineValueObject[queryArray[1]] === primaryKeyOfRecordHoldingCandidateUniqueValue)
{
// The record holding our candidate unique field value has been deleted so...
// The candidate unique field value is available.
uniqueValuesAlreadyUsed = false;
// There is no more reason to track this record.
primaryKeyOfRecordHoldingCandidateUniqueValue = false;
}
// This is not the record we are trying to modify.
// Remove this record from the map.
shouldDeleteThisRecord = true;
}
else // The record was not deleted
{
// If the current record does not have a primary key matching the record we wish to change:
if(lineValueObject[queryArray[1]] != queryArray[3])
{
// Check if this record has the same unique field value we wish to write.
// In other words: Has the unique field value already been used?
if(lineValueObject[data.uniqueField01Name] === data.uniqueField01Value)
{
// Currently this unique field value is taken.
// As we proceed, we may encounter a record with this same primary key where the unique field value has been changed.
// Or, with this same primary key that has been deleted.
// Either case will make the unique field value available again as we proceed through the records
// So we need to compare the primary key of this record to other records that we encounter.
// Flag that the unique field value is already being used.
uniqueValuesAlreadyUsed = true;
// Take note of the primary key so that we can check as we proceed if this record gets
// deleted or if the unique field value gets changed.
primaryKeyOfRecordHoldingCandidateUniqueValue = lineValueObject[queryArray[3]];
} // End of: Check if this record has the same unique field value we wish to write. Is the unique field value already taken?
// Well then - Not deleted, not the same key as the record we want to change, not the candidate unique field value so...
// Check if this record was previously holding the candidate unique field value but is now changed.
else if
(
primaryKeyOfRecordHoldingCandidateUniqueValue === lineValueObject[queryArray[1]]
&&
lineValueObject[data.uniqueField01Name] != data.uniqueField01Value
)
{
// This record was tying up the candidate unique field value but is no longer holding it.
// The candidate unique field value is available again.
uniqueValuesAlreadyUsed = false;
// There is no more reason to track this record.
primaryKeyOfRecordHoldingCandidateUniqueValue = false;
}
// This is not the record we are trying to modify.
// Remove this record from the map.
shouldDeleteThisRecord = true;
} // End of: If the current record does not have a primary key matching the record we wish to change:
} //End of: else - The record was not deleted
// If the record was not marked for deletion and has the primary key of the record we wish to change:
// This is the record we are going to send back to the calling function.
if(shouldDeleteThisRecord == false)
{
// Update this record in the map.
tableMap.set(primaryKey, lineValueObject);
}
else // shouldDeleteThisRecord is true. This is not the record we are trying to modify. Don't send it back.
{
tableMap.delete(primaryKey);
}
}); // End of: readInterface.on('line', function(line){...}
// End of: Look at each record...
// This listener fires after we have looked through all the records in the table file.
// The callback function defined here will stream one record back to the clients browser.
readInterface.on('close', function()
{
// This readable stream will be used to write the result of the merge to a new file.
const sourceStream = new Readable();
// Check that the specified record was found.
if(tableMap.size === 0)
{
helpers.log
(
4,
'xwmv16fc90bzrbnvhbg5' + '\n' +
'No record found for primary key' + '\n'
);
//params: error
return callback("No record found for primary key");
}
if(uniqueValuesAlreadyUsed === true)
{
helpers.log
(
4,
'xwmv16fc90bzrbnvhbg5' + '\n' +
'This ' + data.uniqueField01Name + ' value already exists' + '\n'
);
//params: error
return callback('This ' + data.uniqueField01Name + ' value already exists');
}
for (const [key, valueObject] of tableMap)
{
// Convert the data object to a string.
let stringData = JSON.stringify(valueObject);
// Load the readable stream with data.
sourceStream.push(stringData + '\n');
}
// Tell the stream no more data is coming.
sourceStream.push(null);
//params: error, json record
callback(false, sourceStream);
}); // End of: readInterface.on('close', function(){...}
}; // End of: helpers.getMostRecent = function(data, callback){...}
// End of: // Define a function to retrieve the most current record in a table for a given primary key.
我建议阅读 https://alligator.io/js/async-functions/ 或类似教程以更好地理解 async/await
。有了它,您可以潜在地重写您的函数并使您的流程更易于使用。
使用 async/await
将回调函数重写为函数的简单示例如下所示:
没有async/await
:
function consumerFunction() {
myFunction(1, function(error, value) {
console.log(error, value); // outputs: null, 2
});
}
function myFunction(value, callback) {
setTimeout(() => { // mimick async behaviour
callback(null, value + 1);
}, 1000);
}
与async/await
:
async function consumerFunction() {
try {
const value = myFunction(1);
console.log(value); // outputs 2
} catch (error) {
// Code to handle potential errors
}
}
function myFunction(value) {
return new Promise(function (resolve, reject) {
setTimeout(() => { // mimick async behavoir
resolve(value + 1); // success case
// reject('Something bad happened'); // error case
}, 1000);
})
}
这在 NodeJS 中有效,但我必须在创建它的代码块中使用 recordObject。
即使在函数的开头声明了 recordObject,它在代码块之外也是不可见的,因为函数在检索到 recordObject 之前继续执行。我想使用 async/await 执行此操作,但我不知道从 helpers.getMostRecent.
这是整个函数。
有人可以告诉我如何使用 Async/Await 来做到这一点吗?
谢谢,约翰
// Define a function which builds a webpage and all supporting client/server code for
// adding records to a json table in the database.
meta.build.AddWebpage = function(tableId)
{
let dataObject = {};
dataObject.uniqueField01Value = "";
dataObject.uniqueField01Name = "table.tableName";
dataObject.path = '/dbMetadata/metadata.json';
dataObject.queryString = 'WHERE:;tableId:;MatchesExactly:;' + tableId + ':;';
// Collect information about the webpage from the metadata.
// 1. Look in metadata.json - Read the object for the given tableId.
helpers.getMostRecent(dataObject, function(errorFromGetMostRecent, payload)
{
if(!errorFromGetMostRecent) // Got the most recent record from getMostRecent
{
// Used to decode the payload buffer into readable text.
let decoder = new StringDecoder('utf8');
// This instance of the Writable object gives us a place for a callback to run when the payload is received.
const writable = new Writable();
// Called by pipeline below. Does something useful with the payload
writable.write = function(payload)
{
let stringContainer = '';
stringContainer = stringContainer + decoder.write(payload);
let recordObject = JSON.parse(stringContainer);
// recordObject is all the metadata for the table.
// I can use recordObject to build the webpage and all supporting code for adding records to a table
///////////////My problem is I have to do everything else in this code block////////////////////
?????????????????????????????How can I change this to Async/Await???????????????????????????????
}; // End of: writable.write = function(payload){...}
// Passes the payload stream to the writable object which calls writable.write
// which does something useful with the payload.
pipeline
(
payload,
writable,
function(error){if(error){console.log('There was an error.');}}
);
} // End of: if(!error) Got the most recent record from gitHashedPass
else // There was indeed an error returned by getMostRecent when attempting to get the most current record.
{
helpers.log // Log the error.
(
7,
'bxpa2p2t7ps3wrd1dqu7' + '\n' +
'The following was the error message from getMostRecent:' + '\n' +
errorFromGetMostRecent + '\n'
); // End of: helpers.log // Log the error.
} // End of: Else // There was indeed an error returned by getHashedPass when attempting to get the most current record.
}); //End of: helpers.getMostRecent(dataObject, function(errorFromGetMostRecent, payload)
// Assemble the webpage string from the metadata in recordObject
}// End of: meta.build.AddWebpage = function(tableId){...}
这里是函数 getMostRecent,它被上面的函数调用以获取关于我们希望添加到会计系统的 table 的 json 记录。
// Define a function to retrieve the most current record in a table for a given primary key.
// Serves as table validation for put handlers (editing existing records)
// Checks that a record with the supplied primary key exists to modify.
// Also checks that a candidate values supplied for a unique fields are not already in the table. Enforces uniqueness.
// Streams the most current record back to the calling function.
helpers.getMostRecent = function(data, callback)
{
// No value set but declared here because we need access to these thoughout the entire function.
let queryArray, queryString;
// typical example of a queryString: 'WHERE:;userId:;MatchesExactly:;' + userId + ':;'
queryString = data.queryString
// Make an array out of the queryString where each phrase of the query is an element.
queryArray = queryString.split(":;");
// Create an empty map data structure which will be used to merge records with the same primary key.
// Hard if not impossible do that with objects.
let tableMap = new Map();
// Create a variable to track whether or not candidate values for any unique fields have already been used.
let uniqueValuesAlreadyUsed = false;
// Create a variable to track the primary key of a record that we may encounter which
// is holding a candidate unique value (perhaps and email address or phone number).
// If we encounter this primary key again as we proceed through the records then we will
// check to see if the candidate unique value has been changed or if the record has been deleted.
// If so we will set this variable to false signifying that the candidate unique value is available again.
let primaryKeyOfRecordHoldingCandidateUniqueValue = false
// This function sets up a stream where each chunk of data is a complete line in the table file.
let readInterface = readline.createInterface
(
{ // specify the file to be read.
input: fs.createReadStream(helpers.baseDir + data.path)
}
);
// Look at each record in the file.
readInterface.on('line', function(line)
{
// Convert the JSON string (a single line from the table file) into lineValueObject.
let lineValueObject = JSON.parse(line);
// Declare a variable to serve as a key in the map to manage the lineValueObject.
let primaryKey = lineValueObject[queryArray[1]];
let shouldDeleteThisRecord = false;
if(lineValueObject.deleted === true) // if the record in the table file had the delete field set to true:
{
// If this record was holding our candidate unique field value:
if(lineValueObject[queryArray[1]] === primaryKeyOfRecordHoldingCandidateUniqueValue)
{
// The record holding our candidate unique field value has been deleted so...
// The candidate unique field value is available.
uniqueValuesAlreadyUsed = false;
// There is no more reason to track this record.
primaryKeyOfRecordHoldingCandidateUniqueValue = false;
}
// This is not the record we are trying to modify.
// Remove this record from the map.
shouldDeleteThisRecord = true;
}
else // The record was not deleted
{
// If the current record does not have a primary key matching the record we wish to change:
if(lineValueObject[queryArray[1]] != queryArray[3])
{
// Check if this record has the same unique field value we wish to write.
// In other words: Has the unique field value already been used?
if(lineValueObject[data.uniqueField01Name] === data.uniqueField01Value)
{
// Currently this unique field value is taken.
// As we proceed, we may encounter a record with this same primary key where the unique field value has been changed.
// Or, with this same primary key that has been deleted.
// Either case will make the unique field value available again as we proceed through the records
// So we need to compare the primary key of this record to other records that we encounter.
// Flag that the unique field value is already being used.
uniqueValuesAlreadyUsed = true;
// Take note of the primary key so that we can check as we proceed if this record gets
// deleted or if the unique field value gets changed.
primaryKeyOfRecordHoldingCandidateUniqueValue = lineValueObject[queryArray[3]];
} // End of: Check if this record has the same unique field value we wish to write. Is the unique field value already taken?
// Well then - Not deleted, not the same key as the record we want to change, not the candidate unique field value so...
// Check if this record was previously holding the candidate unique field value but is now changed.
else if
(
primaryKeyOfRecordHoldingCandidateUniqueValue === lineValueObject[queryArray[1]]
&&
lineValueObject[data.uniqueField01Name] != data.uniqueField01Value
)
{
// This record was tying up the candidate unique field value but is no longer holding it.
// The candidate unique field value is available again.
uniqueValuesAlreadyUsed = false;
// There is no more reason to track this record.
primaryKeyOfRecordHoldingCandidateUniqueValue = false;
}
// This is not the record we are trying to modify.
// Remove this record from the map.
shouldDeleteThisRecord = true;
} // End of: If the current record does not have a primary key matching the record we wish to change:
} //End of: else - The record was not deleted
// If the record was not marked for deletion and has the primary key of the record we wish to change:
// This is the record we are going to send back to the calling function.
if(shouldDeleteThisRecord == false)
{
// Update this record in the map.
tableMap.set(primaryKey, lineValueObject);
}
else // shouldDeleteThisRecord is true. This is not the record we are trying to modify. Don't send it back.
{
tableMap.delete(primaryKey);
}
}); // End of: readInterface.on('line', function(line){...}
// End of: Look at each record...
// This listener fires after we have looked through all the records in the table file.
// The callback function defined here will stream one record back to the clients browser.
readInterface.on('close', function()
{
// This readable stream will be used to write the result of the merge to a new file.
const sourceStream = new Readable();
// Check that the specified record was found.
if(tableMap.size === 0)
{
helpers.log
(
4,
'xwmv16fc90bzrbnvhbg5' + '\n' +
'No record found for primary key' + '\n'
);
//params: error
return callback("No record found for primary key");
}
if(uniqueValuesAlreadyUsed === true)
{
helpers.log
(
4,
'xwmv16fc90bzrbnvhbg5' + '\n' +
'This ' + data.uniqueField01Name + ' value already exists' + '\n'
);
//params: error
return callback('This ' + data.uniqueField01Name + ' value already exists');
}
for (const [key, valueObject] of tableMap)
{
// Convert the data object to a string.
let stringData = JSON.stringify(valueObject);
// Load the readable stream with data.
sourceStream.push(stringData + '\n');
}
// Tell the stream no more data is coming.
sourceStream.push(null);
//params: error, json record
callback(false, sourceStream);
}); // End of: readInterface.on('close', function(){...}
}; // End of: helpers.getMostRecent = function(data, callback){...}
// End of: // Define a function to retrieve the most current record in a table for a given primary key.
我建议阅读 https://alligator.io/js/async-functions/ 或类似教程以更好地理解 async/await
。有了它,您可以潜在地重写您的函数并使您的流程更易于使用。
使用 async/await
将回调函数重写为函数的简单示例如下所示:
没有async/await
:
function consumerFunction() {
myFunction(1, function(error, value) {
console.log(error, value); // outputs: null, 2
});
}
function myFunction(value, callback) {
setTimeout(() => { // mimick async behaviour
callback(null, value + 1);
}, 1000);
}
与async/await
:
async function consumerFunction() {
try {
const value = myFunction(1);
console.log(value); // outputs 2
} catch (error) {
// Code to handle potential errors
}
}
function myFunction(value) {
return new Promise(function (resolve, reject) {
setTimeout(() => { // mimick async behavoir
resolve(value + 1); // success case
// reject('Something bad happened'); // error case
}, 1000);
})
}