fetch icon indicating copy to clipboard operation
fetch copied to clipboard

一个基于 React-Native 0.75 Expo 51 的成功案例。

Open ContinueOneSecond opened this issue 1 year ago • 2 comments

首先感谢该项目的所有开源贡献者 环境:

  • React-Native 0.75
  • Expo 51

先说为什么还是选择了fetch:rn-sse、event-source 同样是非常优秀的开源项目,但是不太满足我的HttpRequest需求,以及EventSource会使我的Markdown混乱,我无法解决。

在我尝试了 axion、websocket、rn-sse、event-source 以及数个小时的调试和大量的翻译工作后,终于在这个项目中取得到了结果。

在各种论坛上徘徊后,我找到了该项目,以下是我最低限度的实践:

  1. 我安装了文档中所有的依赖,并且在项目根目录创建了 index.js 这里有几个地方需要说明: import { polyfill as polyfillReadableStream } from 'react-native-polyfill-globals/src/readable-stream'; web-streams-polyfill/ponyfill/es6 加载时会报错,于是我使用 const { ReadableStream } = require('web-streams-polyfill/dist/ponyfill.es5.js'); 代替

polyfillEncoding(); 使用 react-native-polyfill-globals 提供的

polyfillFetch(); 使用 react-native-polyfill-globals 提供的

参考:

https://stackoverflow.com/questions/56207968/stream-api-with-fetch-in-a-react-native-app/77089139#77089139 另外我遇到了一些其他上面的异常,但是介于当前是最小可行性的示例,不进行赘述。

index.js

// import 'react-native-polyfill-globals/auto';
// import { polyfill as polyfillBase64 } from 'react-native-polyfill-globals/src/base64';
// import { polyfill as polyfillReadableStream } from 'react-native-polyfill-globals/src/readable-stream';
// import { polyfill as polyfillURL } from 'react-native-polyfill-globals/src/url';
// import { polyfill as polyfillCrypto } from 'react-native-polyfill-globals/src/crypto';
import { polyfillGlobal } from 'react-native/Libraries/Utilities/PolyfillFunctions';
import { polyfill as polyfillEncoding } from 'react-native-polyfill-globals/src/encoding';
import { polyfill as polyfillFetch } from 'react-native-polyfill-globals/src/fetch';
const { ReadableStream } = require('web-streams-polyfill/dist/ponyfill.es5.js');
// 调用 polyfill 函数
polyfillFetch();
// polyfillReadableStream();
// polyfillBase64();
// polyfillURL();
// polyfillCrypto();
// polyfillFetch();
polyfillEncoding();

polyfillGlobal("ReadableStream", () => ReadableStream);
// polyfillGlobal(
//     "fetch",
//     () =>
//         (...args: any[]) =>
//             fetch(args[0], { ...args[1], reactNative: { textStreaming: true } }),
// );
// polyfillGlobal("Headers", () => Headers);
// polyfillGlobal("Request", () => Request);
// polyfillGlobal("Response", () => Response);
// polyfillGlobal('TextEncoder', () => TextEncoder);
// polyfillGlobal('TextDecoder', () => TextDecoder);

import "expo-router/entry";
  1. 请求示例
 static async post_stream(url: string, params?: Record<string, any>): Promise<ReadableStreamDefaultReader<Uint8Array>> {
        const token = await this.getToken();
        try {
            const response = await fetch(url, {
                method: 'post',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': token ? `Bearer ${token.token}` : '',
                    'Accept': 'text/event-stream'
                },
                //@ts-ignore
                reactNative: { textStreaming: true },
                body: JSON.stringify(params)
            });

            //检查普通异常
            checkStream(response);
            if (response.body === null) {
                throw new BizError("500", "网络错误,请稍后重试");
            }
            return response.body.getReader();
        } catch (error) {
            throw checkError(error);
        }
    }
  1. 解析示例
export const useStreamReader = async ({reader, onData,onOpen, onError, onComplete}: StreamReaderProps) => {
    let isReading = false;
    const decoder = new TextDecoder('utf-8');

    try {
        isReading = true;
        onOpen?.();
        while (isReading) {
            const {done, value} = await reader.read();
            console.log("org",value);
            if (done) {
                onComplete?.();
                break;
            }
            const decodedData = decoder.decode(value);
            console.log("before",decodedData)

            let strings = decodedData.split("data:");
            let res = "";
            strings.forEach((str) => {
                if(str === ''){
                    return;
                }
                if (str.endsWith("\n\n")) {
                    str = str.slice(0, -2);
                }
                const data = str.replace("data:","");
                res += data;
            })
            onData(res);
        }
    } catch (error) {
        console.error(error);
        isReading = false;
        onError?.(error);
    } finally {
        isReading = false;
        onComplete?.();
    }
}

希望可以帮助到有需要的人。

ContinueOneSecond avatar Oct 20 '24 18:10 ContinueOneSecond

Great, modified a little bit with OpenAI streaming support

import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { polyfill as polyfillEncoding } from "react-native-polyfill-globals/src/encoding";
const { ReadableStream } = require("web-streams-polyfill/dist/ponyfill.es5.js");
const rnFetch = require("react-native-fetch-api");

polyfillEncoding();

polyfillGlobal("ReadableStream", () => ReadableStream);
polyfillGlobal(
  "fetch",
  () =>
    (...args) =>
      rnFetch.fetch(args[0], {
        ...args[1],
        reactNative: { textStreaming: true },
      }),
);
polyfillGlobal("Headers", () => rnFetch.Headers);
polyfillGlobal("Request", () => rnFetch.Request);
polyfillGlobal("Response", () => rnFetch.Response);

zlwu avatar Dec 02 '24 15:12 zlwu

在使用这个库时会出现这个问题,有相同的情况吗?[Error: Error: Value is undefined, expected a String]

KongXiao-H avatar Apr 22 '25 06:04 KongXiao-H