Асинхронное/ожидание в NodeJS

Это работает в NodeJS, но мне приходится использовать объект записи внутри блока кода, в котором он был создан.
Он не виден за пределами блока кода, даже если объект записи объявлен в начале функции, потому что функция выполняется до того, как объект записи был создан. получено. Я хочу сделать это с помощью 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, которая вызывается функцией выше, чтобы получить запись 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.


person John Shearing    schedule 06.12.2019    source источник


Ответы (1)


Я бы посоветовал прочитать 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);
  })
}
person tomahaug    schedule 06.12.2019
comment
Спасибо @tomahaug. Начинаю видеть это на вашем примере. Я уже использую async/await для передачи потоков из NodeJS в браузер с помощью fetch. Теперь я вижу на вашем примере, что должно быть обещание и что мои выборки были обещаниями. Теперь я думаю, что мне просто нужно понять, что в приведенном выше коде должно быть обернуто обещанием и куда должно идти финальное ожидание. Спасибо, что указали мне правильное направление. - person John Shearing; 07.12.2019
comment
Похоже, ты на правильном пути. Удачи! - person tomahaug; 11.12.2019