Облачная функция Firebase (асинхронная) не возвращает результат

#firebase #google-cloud-firestore #google-cloud-functions

#firebase #google-cloud-firestore #google-cloud-функции

Вопрос:

Я довольно новичок в функциях Firebase / Firestore / Cloud и пробовал небольшой проект, в котором клиентское приложение вызывает облачную функцию Firebase для генерации некоторых случайных ключей (случайных чисел), добавляет их в Firestore и при успешной записи возвращает эти ключи в клиентское приложение. Вроде как генератор случайных чисел.

Функция правильно вызывается клиентом (в соответствии с Firebase Console), генерирует ключи, проверяет, существуют ли они в Firestore, и, если нет, добавляет их. Все работает до той части, где он должен возвращать результат клиенту. Здесь клиент никогда не получает результат (массив ключей). Фактически, обратный вызов в клиентском приложении (iOS / Swift) никогда не вызывается.

Я подозреваю, что проблема заключается в возврате обещания? Согласно документации Firebase здесь, асинхронные вызываемые объекты должны возвращать обещание, хотя я не совсем уверен, что то, что я делаю, правильно https://firebase.google.com/docs/functions/callable

Вот код облачной функции:

 export const generateRandomKeys = functions.https.onCall(async (data, context) => {

  // Read data passed from client
  const numberOfKeys = data.numberOfKeys
  console.log("Number of keys to generate: ", numberOfKeys)

  // Generate some keys
  const generatedKeys = KeyMaker.newKeys(numberOfKeys)

  try {

    const randomkeys = []

    // Write keys to DB
    for (const key of generatedKeys) {
      const addedKey = await writeKeyToDB(key)
      randomkeys.push(addedKey)
    }

    return Promise.resolve(JSON.stringify(randomkeys))

  } catch (error) {
      console.log("Error has occured: ", error)
      throw new Error("An Error has occured: "   error)
  }
})


async function writeKeyToDB(key: string){

  try {
    // Check if a document with the same key already exists in the DB
    const docRef = db.collection("randomKeys").doc(key)
    const doc =  await docRef.get()

    // Document with same key found!
    if (doc.exists) {
      // Generate a new key and try again
      const newKey = KeyMaker.newKey()

      console.log("Will generate a new key and try again!. New key: ", newKey)
      await writeKeyToDB(newKey)
    }

    const keyDoc = {
      somefield: somevalue,     
    }

    // Write to DB then return result
    await docRef.set(keyDoc)

    return Promise.resolve(key)

  } catch (error) {
      return Promise.reject(error)
  }
}

  

Клиент (Swift)

      public static func generateNewRandomNumbers(numberOfKeys: Int) {

        FirebaseApp.configure()

        let functions = Functions.functions(region: FIRESTORE_REGION)

        functions.httpsCallable("generateRandomKeys").call(["numberOfKeys": numberOfKeys]) { (result, error) in

            // Nothing here executes                                                    
            print("----------got reply---------")
            if let error = error as NSError? {
                if error.domain == FunctionsErrorDomain {
                    let code = FunctionsErrorCode(rawValue: error.code)
                    let message = error.localizedDescription
                    print("Error (String(describing: code)): "   message)
                }
            }

            if let keys = (result?.data as? [String]) {
                dump(keys)
            }
        }
    }

  

Комментарии:

1. Если вы используете async / await, вам не нужно явно возвращать обещание. Возвращаемое значение будет автоматически завернуто в обещание. Но я не знаю, проблема ли это здесь.

2. Спасибо за быстрый ответ. Я удалил обещания и вернул напрямую значения, но, похоже, это ничего не изменило. Я добавил в OP код клиента, мне кажется, что там нет проблемы

3. @DougStevenson Теперь я наконец получил результат в клиентском приложении, я могу распечатать его, используя dump(results?.data) какие результаты, Optional(["838483","33411","94949"]) но я не могу определить тип результатов. данные для его чтения. Я пытался [String] , но, похоже, это не так. Какой тип это будет?

Ответ №1:

Не объединяйте Async/Await и Promise . Async функционирует как сам возврат Promise .

Сначала измените возврат вашей облачной функции на :

 return JSON.stringify(randomkeys);
  

Также в writeKeyToDb изменении возврат к:

 return key;
  

и catch часть для:

 throw Error(error);
  

Я также вижу проблему в том, что в облачной функции вы вызываете свою writeKeyToDb функцию с 2 параметрами, но у этой функции есть только один. Но этот код, вероятно, выполняется

Комментарии:

1. Внес изменения, но не помогло. Что касается идентификатора пользователя, в моем исходном коде было еще несколько переменных, и я немного изменил его, чтобы упростить код для публикации здесь. Итак, я забыл пару вещей здесь и там. Но исходный код работает точно так же. Я также опубликовал код клиента. Есть идеи, почему функция async может не возвращаться?

2. Ну, вы пытались обменять этот возврат облачной функции на что-то статическое return ['onekey','secondKey'] ?

3. Вы совершенно правы. Я попытался просто вернуть только значение, и оно действительно не вернулось, поэтому я еще раз взглянул на код клиента и по ошибке внес изменения в неправильный класс swift. Итак, теперь я получаю ответный вызов, но теперь я не знаю, как проанализировать result.data. Когда я это делаю dump(result?.data) , я вижу ключи в консоли XCode, но if let licenseKeys = (result?.data as? [String]) не выполняется. Разве результат не является массивом строк?

4. @UMAD создайте для этого новый билет. Потому что это не ошибка облачной функции

Ответ №2:

Наконец-то нашел проблему, спасибо Дугу и Доминику за то, что они направили меня в правильном направлении. Я удалил обещания и вернул непосредственно значения, но, что более важно, мне не нужно было преобразовывать массив в JSON. Я наткнулся на документацию HTTPSCallableResult

Я просто изменил это

return JSON.stringify(randomkeys);

Для

return randomkeys

и на клиенте, вместо

if let keys = (result?.data as? [String]) {
dump(keys)
}

Я делаю

if let keys = (result?.data as? NSArray) {
dump(keys)
}