Стиль обратного вызова узла с обещанием?«Исходный аргумент должен иметь тип функции» - PullRequest
0 голосов
/ 12 февраля 2019

Я использую util.promisify в облачной функции Google для вызова функции преобразования текста в речь IBM Watson, которая возвращает обратный вызов.Мой код работает, но я получаю сообщение об ошибке:

TypeError [ERR_INVALID_ARG_TYPE]: The "original" argument must be of type function

Документация говорит

Принимает функцию в соответствии с общим стилем обратного вызова с ошибкой, т.е.принимает обратный вызов (err, value) => ... в качестве последнего аргумента и возвращает версию, которая возвращает обещания.

Обратный вызов IBM Watson сложен, и я не могу понять, как его реорганизовать в Node.jsстиль обратного вызова.Это работает, я должен просто игнорировать сообщение об ошибке?Вот моя функция Google Cloud:

exports.IBM_T2S = functions.firestore.document('Users/{userID}/Spanish/IBM_T2S_Request').onUpdate((change) => {

    let word = change.after.data().word;
    let wordFileType = word + '.mp3';

    function getIBMT2S(word, wordFileType) {
      const {Storage} = require('@google-cloud/storage');
      const storage = new Storage();
      const bucket = storage.bucket('myProject.appspot.com');
      const file = bucket.file('Audio/Spanish/Latin_America/' + wordFileType);
      var util = require('util');
      var TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');

      var textToSpeech = new TextToSpeechV1({
        username: 'groucho',
        password: 'swordfish',
        url: 'https://stream.watsonplatform.net/text-to-speech/api'
      });

      var synthesizeParams = {
        text: word,
        accept: 'audio/mpeg',
        voice: 'es-LA_SofiaVoice',
      };

      const options = { // construct the file to write
        metadata: {
          contentType: 'audio/mpeg',
          metadata: {
            source: 'IBM Watson Text-to-Speech',
            languageCode: 'es-LA',
            gender: 'Female'
          }
        }
      };

      textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
        console.log(error);
      }).pipe(file.createWriteStream(options))
      .on('error', function(error) {
        console.error(error);
      })
      .on('finish', function() {
        console.log("Audio file written to Storage.");
      });
    };

var passGetIBMT2S = util.promisify(getIBMT2S(word, wordFileType))
passGetIBMT2S(word, wordFileType)
});

Ответы [ 2 ]

0 голосов
/ 17 февраля 2019

Вот мой законченный код.Есть две функции.getT2S вызывает IBM Watson Text-to-Speech, затем записывает аудиофайл в хранилище и получает URL-адрес для загрузки.writeDownloadURL проверяет, существует ли документ Firestore, затем либо sets или updates URL-адрес загрузки для Firestore.

exports.IBM_T2S = functions.firestore.document('Users/{userID}/Spanish/IBM_T2S_Request').onUpdate((change) => {

if (change.after.data().word != undefined) {
  // get requested word object
  let accent = change.after.data().accent;
  let audioType = change.after.data().audioType;
  let gender = change.after.data().gender;
  let longLanguage = change.after.data().longLanguage;
  let shortLanguage = change.after.data().shortLanguage;
  let shortSource = change.after.data().shortSource;
  let source = change.after.data().source;
  let voice = change.after.data().voice;
  let word = change.after.data().word;
  console.log(word);
  let wordFileType = word + '.' + audioType;
  let pronunciation = `${accent}-${gender}-${shortSource}`;

  const {Storage} = require('@google-cloud/storage');
  const storage = new Storage();
  const bucket = storage.bucket('myProject.appspot.com');
  const file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + wordFileType);

  var TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');

  var textToSpeech = new TextToSpeechV1({
    username: 'groucho',
    password: 'swordfish',
    url: 'https://stream.watsonplatform.net/text-to-speech/api'
  });

  var synthesizeParams = {
    text: word,
    accept: 'audio/' + audioType,
    voice: voice
  };

  const options = { // construct the file to write
    metadata: {
      contentType: 'audio/' + audioType,
      metadata: {
        accent: accent,
        audioType: audioType,
        gender: gender,
        longLanguage: longLanguage,
        shortLanguage: shortLanguage,
        source: source,
        voice: voice,
        word: word
      }
    }
  };

  // check if Pronunciations collection exists, set or update to not destroy existing data
  function writeDownloadURL(downloadURL) {
    admin.firestore().collection('Dictionaries').doc(longLanguage).collection('Words').doc(word).collection('Pronunciations').doc(pronunciation).get()
    .then(function(doc) {
      if (doc.exists) {
        return admin.firestore().collection('Dictionaries').doc(longLanguage).collection('Words').doc(word).collection('Pronunciations').doc(pronunciation).update({ audioFile: downloadURL })
        .then(result => console.log('DONE'))
        .catch(error => console.error(error));
      } else {
        return admin.firestore().collection('Dictionaries').doc(longLanguage).collection('Words').doc(word).collection('Pronunciations').doc(pronunciation).set({ audioFile: downloadURL })
        .then(result => console.log('DONE'))
        .catch(error => console.error(error));
      } // close else
    })
    .catch(error => console.error(error));
  } // close writeDownloadURL

  // documentation at https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises
  function getT2S(synthesizeParams) {
    return new Promise(function(resolve, reject) {
      // documentation at https://cloud.ibm.com/apidocs/text-to-speech?code=node#synthesize-audio-get
      textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
        console.error(error);
        reject(error);
      }).pipe(file.createWriteStream(options))
      .on('error', function(error) {
        console.error(error);
        reject(error);
      })
      .on('finish', function() {
        resolve(file.getSignedUrl({
          action: 'read',
          expires: '03-17-2025'
        }));
      }); // close on finish
    }); // close Promise
  } // close getT2SAsync

  async function getT2SAsync(synthesizeParams) {
    var signedUrls = await getT2S(synthesizeParams);
    var downloadURL = signedUrls[0];
    await writeDownloadURL(downloadURL);
    console.log("All done.");
  }

  return getT2SAsync(synthesizeParams);

} else { // if no word passed to function
  console.error("Error.");
}
}); // close IBM_T2S

Я ошибочно написал

 return file.getSignedUrl({

вместо

 resolve(file.getSignedUrl({

В результате данные не были возвращены из обещания, и облачная функция отключилась по истечении шести секунд без завершения выполнения.Вы должны что-то сделать с resolve.Я использовал reject дважды, чтобы быть уверенным.: -)

0 голосов
/ 12 февраля 2019

Это работает, потому что вы вызываете getIBMT2S и передаете возвращаемое значение util.promisfy, а не саму функцию.

Здесь есть несколько проблем, во-первых, ваша функция getIBMT2S неПохоже, он будет совместим с util.promisfy, как вы отметили в документах, совместимая функция должна следовать типичной сигнатуре в стиле обратного вызова (getIBMT2S не принимает параметр обратного вызова).

Во-вторых, util.promisify ожидает function - в вашем случае вместо этого вы передаете return значение функции.Если getIBMT2S был обновлен для поддержки параметра обратного вызова, тогда правильное использование будет

function getIBMT2S(word, wordFileType, cb) {
  ...
  // be sure to invoke cb in here
}
var passGetIBMT2S = util.promisify(getIBMT2S); // <-- don't call getIBMT2S, pass it in directly
passGetIBMT2S(word, wordFileType) // <-- now invoke the wrapped function
  .then(result => console.log('DONE'));
  .catch(e => console.error(e));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...