Вызов вызова (...) не вызывает функцию - PullRequest
0 голосов
/ 06 октября 2018

У меня проблемы с методом redux-saga api call, и он ведет себя так, как задумано.Суть проблемы, которую я считаю, заключается в том, что call не вызывает переданную ей функцию.

Вот основная функция генератора sendEmail, которая запускает вызов API:

/**
 * A POST api call that will send a sendGrid email with csv data as an attachment
 *
 * @param  {object} action object containing the csv data, security key string, fields of CSV, and CSV file name
 *
 */

export function* sendEmail(action) {
  const { payload, security_key, CSVFields, CSVFileName } = action;
  //  API url
  const requestURL = `/api/email?security_key=${security_key}`;
  //  JSON2csvParser options, with the CSV fields
  const opts = { fields: CSVFields };
  //  The CSV data, which is a string
  const CSVData = payload;

  try {
    const parser = new Json2csvParser(opts);
    const csv = parser.parse(CSVData);
    //  create blob with the csv string
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });

    //  instantiate File Reader Web API
    const reader = new FileReader();

    //  when reader finishes reader.readAsDataURL(blob), stepthrough the emailAPI generator
    reader.addEventListener(
      'load',
      () => {
        const gen = emailAPI(requestURL, CSVFileName, reader);
        console.log("gen.next(): ", gen.next());
        console.log("gen.next(): ", gen.next());
        console.log("gen.next(): ", gen.next());
      },
      false,
    );

    //  reader reads the contents of the blob
    reader.readAsDataURL(blob);

    //  error handling of the reader
    reader.onerror = function (error) {
      console.log('Error: ', error);
    };

  } catch (err) {
    console.error(
      'sendEmail: Error occurred while parsing JSON to CSV ',
      err,
    );
  }
}

Эта функция генератора sendEmail вызовет другую функцию генератора emailAPI.

Это код для этой функции генератора emailAPI:

function* emailAPI(url, filename, reader) {
  let readerResult = null
  yield readerResult = reader.result.split(',')[1]
  const requestBody = {
    filename,
    content: readerResult,
  };

  try {
    const response = yield call(request, url, {
      method: 'POST',
      body: JSON.stringify(requestBody),
      headers: new Headers({
        'Content-Type': 'application/json',
        Accept: 'application/json',
      }),
    });
    console.log("response: ", response);
  } catch (err) {
    console.log("err: ", err);
    yield err
  }
}

Переход по gen.next(), вот что я получаю в журналах:

imagegen.next()">

Как вы увидите на рисунке, значение первого итератора возвращает readerResult успешно.Значение второго итератора возвращает сагу с избыточностью call.Перед последним итератором (то есть когда генератор завершен) я записываю response, , который возвращает неопределенное значение , как и последний итератор.

Функция, переданная в call, request, работает с другими сагами.Я регистрируюсь в функции request, чтобы проверить, вызывается ли она. Это не .Это поведение, которого я не ожидаю, кто-нибудь знает, почему request не вызывается?

EDIT Вот функция request.Просто fetch передается URL и параметры.

import 'whatwg-fetch';

/**
 * Parses the JSON returned by a network request
 *
 * @param  {object} response A response from a network request
 *
 * @return {object}          The parsed JSON from the request
 */
function parseJSON(response) {
  if (response.status === 204 || response.status === 205) {
    return null;
  }
  return response.json();
}

/**
 * Checks if a network request came back fine, and throws an error if not
 *
 * @param  {object} response   A response from a network request
 *
 * @return {object|undefined} Returns either the response, or throws an error
 */
function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  const error = new Error(response.statusText);
  error.response = response;
  throw error;
}

/**
 * Requests a URL, returning a promise
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 *
 * @return {object}           The response data
 */
export default function request(url, options) {
  console.log("request    url: ", url);
  return fetch(url, options)
    .then(checkStatus)
    .then(parseJSON);
}

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

По сути, к сожалению, вы можете не использовать yield call(fn,arguments) в обратном вызове в функции генератора (в данном случае sendEmail), поскольку ожидается, что обратный вызов будет обычной функцией, а не генератором.one.

Вместо этого вы создаете eventChannel в закрытии функции генератора (sendEmail) и передаете eventChannel.emit в качестве обратного вызова.Затем вы слушаете этот eventChannel, где вы можете использовать yield call(fn,arguments)

Более простой вариант использования будет более понятным https://redux -saga.js.org / docs / advanced/Channels.html

<!-- language: lang-js -->
import { eventChannel, delay } from "redux-saga";

function* sendEmail(action) {
  const { payload, security_key, CSVFields, CSVFileName } = action;

  //  JSON2csvParser options, with the CSV fields
  const opts = { fields: CSVFields };
  //  The CSV data, which is a string
  const CSVData = payload;

  try {
    const parser = new Json2csvParser(opts);
    const csv = parser.parse(CSVData);
    //  create blob with the csv string
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    //  instantiate File Reader Web API
    const reader = new FileReader();

    const loadChannel = eventChannel(emit => {
      reader.addEventListener("load", emit, false);
      const unsubscribeFn = () => reader.removeEventListener("load");
      return unsubscribeFn;
    });
    yield spawn(onReaderLoad, loadChannel);
    //  reader reads the contents of the blob
    reader.readAsDataURL(blob);
    //  error handling of the reader
    reader.onerror = console.log;
  } catch (err) {
    console.error("sendEmail: Error occurred while parsing JSON to CSV ", err);
  }
}

function* onReaderLoad(channel) {
  while (true) {
    const event = yield take(channel);
    const content = event.target.result.split(",")[1];
    const requestURL = `/api/email?security_key=${security_key}`;
    const requestBody = {
      CSVFileName,
      content
    };

    try {
      const response = yield call(request, requestURL, {
        method: "POST",
        body: JSON.stringify(requestBody),
        headers: new Headers({
          "Content-Type": "application/json",
          Accept: "application/json"
        })
      });
      console.log("response: ", response);
    } catch (err) {
      console.log("err: ", err);
      yield err;
    }
  }
}

Я знаю, что вам все равно нужно передать CSVFileName в генератор onReaderLoad, но я думаю, что это было бы хорошим упражнением для вас.

Я передал emit в качестве обратного вызова reader.addEventListener("load", emit, false); , но вместо этого мы могли бы передать анонимный fn и передать дополнительные аргументы reader.addEventListener("load", (event)=> emit {CSVFileName, event}, false);

0 голосов
/ 08 октября 2018

Проблема

Проблема в следующих строках:

const gen = emailAPI(requestURL, CSVFileName, reader);
console.log("gen.next(): ", gen.next());
console.log("gen.next(): ", gen.next());
console.log("gen.next(): ", gen.next());

Я полагаю, вы пытались выполнить функцию генератора вручную, поскольку вы не могли использовать yield внутри обработчика событий.redux-saga предлагает eventChannel для подобных ситуаций.

Сначала позвольте мне объяснить, почему вы получили undefined и почему request никогда не вызывали.Это потому, что yield call(request) вернет только дескриптор эффекта, который должен обрабатываться промежуточным программным обеспечением саги.То, что вы здесь делаете, проходит только через генератор и ничего не делает для обработки эффекта вызова.Также в третьем gen.next() вы ничего не передаете (эквивалент undefined) в качестве возвращаемого значения yield.Это undefined, который вы получили в response: undefined консоли.

Решение

Это не полное решение.Я надеюсь, что это поможет вам в правильном направлении.Пожалуйста, попросите пояснения в комментариях, если вам нужно.

Я немного изменил ваш саг-код, чтобы использовать каналы событий.Вам нужно будет улучшить его, чтобы он точно соответствовал вашим требованиям.

...
import { eventChannel } from 'redux-saga';
...

let fileReaderEventChannel;

export function getFileReaderEventChannel(blob) {
  if (fileReaderEventChannel) {
    fileReaderEventChannel = eventChannel((emit) => {

      //  instantiate File Reader Web API
      const reader = new FileReader();

      //  when reader finishes reader.readAsDataURL(blob), stepthrough the emailAPI generator
      reader.addEventListener(
        'load',
        () => {
          emit({ error: null })
        },
        false,
      );

      //  reader reads the contents of the blob
      reader.readAsDataURL(blob);

      //  error handling of the reader
      reader.onerror = function (error) {
        emit({ error });
      };

      return () => { 
        // Properly close or abort FileReader here.
      };

    });
  }
  return fileReaderEventChannel;
};

/**
 * A POST api call that will send a sendGrid email with csv data as an attachment
 *
 * @param  {object} action object containing the csv data, security key string, fields of CSV, and CSV file name
 *
 */

export function* sendEmail(action) {
  const { payload, security_key, CSVFields, CSVFileName } = action;
  //  API url
  const requestURL = `/api/email?security_key=${security_key}`;
  //  JSON2csvParser options, with the CSV fields
  const opts = { fields: CSVFields };
  //  The CSV data, which is a string
  const CSVData = payload;

  try {
    const parser = new Json2csvParser(opts);
    const csv = parser.parse(CSVData);
    //  create blob with the csv string
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });

    // CHECK HERE - START
    const channel = yield call(getFileReaderEventChannel, blob);
    const fileReaderEvent = yield take(channel);
    if (fileReaderEvent.error === null) {
      yield call(emailAPI, requestURL, CSVFileName, reader);
    } else {
      console.log('error', error);
    }
    // CHECK HERE - END

  } catch (err) {
    console.error(
      'sendEmail: Error occurred while parsing JSON to CSV ',
      err,
    );
  }
}

Посмотрите на код между CHECK HERE комментариями и новой функцией getFileReaderEventChannel.

Ссылки:

...