【问题标题】:generator function doesn't resume after yield生成器函数在 yield 后不恢复
【发布时间】:2021-10-14 23:25:55
【问题描述】:

所以我正在尝试调试为什么我的问题数组没有正确填充到我的 mobx-state-tree questions-store 中。 getQuestions 操作中的 yield 语句之后没有任何内容运行。

// question-store.ts
import { Instance, SnapshotOut, types, flow } from "mobx-state-tree"
import { Question, QuestionModel, QuestionSnapshot } from "../question/question";
import { withEnvironment } from '../';
import { GetQuestionsResult } from "../../services/api";

    export const QuestionStoreModel = types
      .model("QuestionStore")
      .props({
        questions: types.optional(types.array(QuestionModel), [])
      })
      .extend(withEnvironment)
      .views((self) => ({})) // eslint-disable-line @typescript-eslint/no-unused-vars
      .actions((self) => ({
        saveQuestions: (questionSnapshots: QuestionSnapshot[]) => {
          console.tron.log("SAVE QUESTIONS");
          console.tron.log({ questionSnapshots });
          const questionModels: Question[] = questionSnapshots.map(c => QuestionModel.create(c));
          self.questions.replace(questionModels); // Replace the existing data with the new data
        }
      })) 
      .actions((self) => ({
        getQuestions: flow(function*() {
          console.tron.log("GET QUESTIONS");
          const result: GetQuestionsResult = yield self.environment.api.getQuestions(); // Nothing after this yield statement runs
          console.tron.log("AFTER GET QUESTIONS");
    
          if (result.kind === "ok") {
            self.saveQuestions(result.questions);
          } else {
            __DEV__ && console.tron.log(result.kind);
          }
        })
      }))
type QuestionStoreType = Instance<typeof QuestionStoreModel>
export interface QuestionStore extends QuestionStoreType {}
type QuestionStoreSnapshotType = SnapshotOut<typeof QuestionStoreModel>
export interface QuestionStoreSnapshot extends QuestionStoreSnapshotType {}
export const createQuestionStoreDefaultModel = () => types.optional(QuestionStoreModel, {})

这是 api 的 getQuestions

import { ApisauceInstance, create, ApiResponse } from "apisauce"
import { getGeneralApiProblem } from "./api-problem"
import { ApiConfig, DEFAULT_API_CONFIG } from "./api-config"
import * as Types from "./api.types"
import uuid from 'react-native-uuid';
import { QuestionSnapshot, Question } from "../../models";

const API_PAGE_SIZE = 5;

const convertQuestion = (raw: any): QuestionSnapshot => {
  const id = uuid.v4().toString();

  return {
    id: id,
    category: raw.category,
    type: raw.type,
    difficulty: raw.difficulty,
    question: raw.question,
    correctAnswer: raw.correct_answer,
    incorrectAnswers: raw.incorrect_answers,
  }
}

/**
 * Manages all requests to the API.
 */
export class Api {
  /**
   * The underlying apisauce instance which performs the requests.
   */
  apisauce: ApisauceInstance

  /**
   * Configurable options.
   */
  config: ApiConfig

  /**
   * Creates the api.
   *
   * @param config The configuration to use.
   */
  constructor(config: ApiConfig = DEFAULT_API_CONFIG) {
    this.config = config
  }

  /**
   * Sets up the API.  This will be called during the bootup
   * sequence and will happen before the first React component
   * is mounted.
   *
   * Be as quick as possible in here.
   */
  setup() {
    // construct the apisauce instance
    this.apisauce = create({
      baseURL: this.config.url,
      timeout: this.config.timeout,
      headers: {
        Accept: "application/json",
      },
    })
  }
    async getQuestions(): Promise<Types.GetQuestionsResult> {
        // make the api call
        const response: ApiResponse<any> = await this.apisauce.get("", { amount: API_PAGE_SIZE })
        console.tron.log({response});
        // the typical ways to die when calling an api
        if (!response.ok) {
          const problem = getGeneralApiProblem(response);
          if (problem) return problem;
        }
        console.tron.log('AFTER OK CHECK')
    
        // transform the data into the format we are expecting
        try {
          const rawQuestion = response.data.results;
          console.tron.log({rawQuestion});
          const convertedQuestions: QuestionSnapshot[] = rawQuestion.map(convertQuestion);
          console.tron.log({convertedQuestions});
          return { kind: "ok", questions: convertedQuestions };
        } catch (e) {
          __DEV__ && console.tron.log(e.message);
          return { kind: 'bad-data' }
        }
      }
  async getUsers(): Promise<Types.GetUsersResult> {
    // make the api call
    const response: ApiResponse<any> = await this.apisauce.get(`/users`)

    // the typical ways to die when calling an api
    if (!response.ok) {
      const problem = getGeneralApiProblem(response)
      if (problem) return problem
    }

    const convertUser = (raw) => {
      return {
        id: raw.id,
        name: raw.name,
      }
    }

    // transform the data into the format we are expecting
    try {
      const rawUsers = response.data
      const resultUsers: Types.User[] = rawUsers.map(convertUser)
      return { kind: "ok", users: resultUsers }
    } catch {
      return { kind: "bad-data" }
    }
  }

  /**
   * Gets a single user by ID
   */

  async getUser(id: string): Promise<Types.GetUserResult> {
    // make the api call
    const response: ApiResponse<any> = await this.apisauce.get(`/users/${id}`)

    // the typical ways to die when calling an api
    if (!response.ok) {
      const problem = getGeneralApiProblem(response)
      if (problem) return problem
    }

    // transform the data into the format we are expecting
    try {
      const resultUser: Types.User = {
        id: response.data.id,
        name: response.data.name,
      }
      return { kind: "ok", user: resultUser }
    } catch {
      return { kind: "bad-data" }
    }
  }
}

问题模型:

import { Instance, SnapshotOut, types } from "mobx-state-tree"
import { shuffle } from "lodash";
    export const QuestionModel = types
      .model("Question")
      .props({
        id: types.identifier,
        category: types.maybe(types.string), 
        type: types.enumeration(['multiple', 'boolean']),
        difficulty: types.enumeration(['easy', 'medium', 'hard']),
        question: types.maybe(types.string), 
        correctAnswer: types.maybe(types.string), 
        incorrectAnswers: types.optional(types.array(types.string), []),
        guess: types.maybe(types.string)
      })
      .views((self) => ({
        get allAnswers() {
          return shuffle(self.incorrectAnswers.concat([self.correctAnswer]))
        },
        get isCorrect() {
          return self.guess === self.correctAnswer;
        }
      })) 
      .actions((self) => ({
        setGuess(guess: string) {
          self.guess = guess;
        }
      })) 
    
    type QuestionType = Instance<typeof QuestionModel>
    export interface Question extends QuestionType {}
    type QuestionSnapshotType = SnapshotOut<typeof QuestionModel>
    export interface QuestionSnapshot extends QuestionSnapshotType {}
    export const createQuestionDefaultModel = () => types.optional(QuestionModel, {})

Debug logs

从日志中可以看出,api 调用成功,convertedQuestions 正确记录,但之后没有任何记录。也许我误解了生成器函数的工作原理,但它不应该在产生函数返回值后恢复吗?

任何见解都将不胜感激。

【问题讨论】:

    标签: typescript react-native expo mobx-state-tree apisauce


    【解决方案1】:

    tl;dr:在 QuestionStoreModel 中检查您对 flow 的导入是否来自 mobx-state-tree,而不是 mobx

    import { flow } from "mobx-state-tree";
    

    你的代码看起来很完美,所以我猜这个问题围绕着flow()的使用。

    来自the mst docs on Asynchronous Actions

    警告:不要从“mobx”导入流,而是从“mobx-state-tree”导入!

    他们没有详细说明如果导入不正确会发生什么,但我认为这会影响产量语句。

    【讨论】:

    • 嗯,不幸的是我似乎是从“mobx-state-tree”导入的。我更新了代码 sn-ps 以包含整个文件,包括导入和导出。
    猜你喜欢
    • 2021-09-05
    • 2020-04-07
    • 2017-08-29
    • 2017-07-07
    • 2023-03-03
    • 1970-01-01
    • 2020-05-25
    • 2016-03-19
    • 2021-04-07
    相关资源
    最近更新 更多