repeaterjs / repeater

The missing constructor for creating safe async iterators
https://repeater.js.org
MIT License
459 stars 12 forks source link

TypeError: Invalid attempt to iterate non-iterable instance. #68

Closed evelant closed 3 years ago

evelant commented 3 years ago

I'm attempting to use repeaters in a react-native typescript project. When I try to for await any iterator it throws the exception

TypeError: Invalid attempt to iterate non-iterable instance.

This happens running the example verbatim from the documentation. Not really sure what's going on here. It must be something about my typescript configuration?

"@repeaterjs/repeater": "^3.0.4",

tsconfig.json

{
    "compilerOptions": {
        "sourceMap": true,
        "allowJs": false,
        "resolveJsonModule": true,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "experimentalDecorators": true,
        "jsx": "react",
        "lib": ["es2019"],
        "moduleResolution": "node",
        "noErrorTruncation": true,
        "noImplicitReturns": true,
        "target": "ESNext",
        "strict": true,
        "noImplicitAny": true,
        "noFallthroughCasesInSwitch": true,
        "noEmit": true,
        "skipLibCheck": true,
        "types": ["node", "jest"],
        "typeRoots": ["./node_modules/@types"]
    },
    "include": ["src"],
    "exclude": [
        "**/node_modules",
        "node_modules",
        "**/node_modules/**/*",
        "**/.*/",
        "babel.config.js",
        "metro.config.js",
        "jest.config.js",
        "ios",
        "android",
        ".history",
        ".tsbuild"
    ]
}
brainkim commented 3 years ago

Hmmmm it looks like you have a runtime error there so it’s less likely to be caused by TypeScript. My guess is that you are attempting to use a repeater as an iterable rather than an async iterable, by forgetting to add an await after for.

// what you might be doing
for (const a of repeater) {
}

// what you should be doing
for await (const a of repeater) {
}

If this isn’t the problem, looking at stack trace of the error and showing some code of how you're using repeaters might help me get a better understanding.

Thanks for trying repeaters!

evelant commented 3 years ago

After I encountered this I thought I might have accidentally been using repeaters incorrectly so I copied the example from the docs verbatim and it crashes at runtime the same way

import { Repeater } from "@repeaterjs/repeater";
const timestamps = new Repeater(async (push, stop) => {
  push(Date.now());
  const interval = setInterval(() => push(Date.now()), 1000);
  await stop;
  clearInterval(interval);
});
(async function() {
  let i = 0;
  for await (const timestamp of timestamps) {
    console.log(timestamp);
    i++;
    if (i >= 10) {
      console.log("ALL DONE!");
      break; // triggers clearInterval above
    }
  }
})();

When the example runs:

TypeError: Invalid attempt to iterate non-iterable instance.
brainkim commented 3 years ago

Does that error come with a stack trace? Or is that all you see? What environment/runtime is this running on? Perhaps there is a transpilation issue where for await isn’t being transpiled correctly?

evelant commented 3 years ago

Aha, I figured it out. Seems that typescript doesn't transpire async iterator support, or at least it doesn't happen when building via metro on react-native. I had to add a babel plugin.

yarn add --dev @babel/plugin-proposal-async-generator-functions

then add to babel.config.js like so

module.exports = {
    presets: ["module:metro-react-native-babel-preset"],
    plugins: [
        ["@babel/plugin-proposal-async-generator-functions"],
    ],
}

Thanks for building this library! The super clean API is a breath of fresh air compared to the insane complexity of rxjs.

brainkim commented 3 years ago

Good to know! Thanks for reporting in.