{"id":21241283,"url":"https://github.com/icyjoseph/async-ops","last_synced_at":"2026-05-20T06:09:12.772Z","repository":{"id":37739000,"uuid":"140068267","full_name":"icyJoseph/async-ops","owner":"icyJoseph","description":"Large asynchronous operations, without blocking the UI","archived":false,"fork":false,"pushed_at":"2020-08-18T15:00:43.000Z","size":764,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-21T19:41:53.807Z","etag":null,"topics":["array-manipulations","asynchronous","javascript","react"],"latest_commit_sha":null,"homepage":"https://helpless-rice.surge.sh","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/icyJoseph.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-07T08:52:52.000Z","updated_at":"2022-06-22T08:48:54.000Z","dependencies_parsed_at":"2022-08-24T16:21:43.029Z","dependency_job_id":null,"html_url":"https://github.com/icyJoseph/async-ops","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fasync-ops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fasync-ops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fasync-ops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fasync-ops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/icyJoseph","download_url":"https://codeload.github.com/icyJoseph/async-ops/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243681005,"owners_count":20330155,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array-manipulations","asynchronous","javascript","react"],"created_at":"2024-11-21T00:55:18.627Z","updated_at":"2026-05-20T06:09:12.746Z","avatar_url":"https://github.com/icyJoseph.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Asynchronous Operations\n\nNext challenge, Reduce, Map and Filter.\n\nDemo available at: [Demo Async Ops](https://helpless-rice.surge.sh)\n\n## Challenge\n\nIs it possible to keep a clock ticking while building a very large array? Say of 10 000 000 elements?\n\nMaking an array of length 10 000 000 synchronously, takes about 2.3 seconds, which means the clock will skip a beat. Find a way to do so without blocking the UI.\n\nInspiration taken from:\n\n- [Chunks](https://stackoverflow.com/questions/10344498/best-way-to-iterate-over-an-array-without-blocking-the-ui)\n- [Async Reducers](https://blog.bloomca.me/2018/01/27/asynchronous-reduce-in-javascript.html)\n\n## Solution\n\n_No webworkers_\n\n`Async/await` and processing chunks.\n\nThis function takes the length of the very large array and the elements (elems) to use when filling the array.\nOptionally a callback funciton to apply against the array when the job is done. If no callback is passed, the array is returned.\nOptionally, also, a timeout, by default set to 200 ms.\n\nThis function, relies on chunk processor, to concat the element into the array in chunks, as long as the time for the task does not run out. This is handled in the while loop, which closes every 200 ms, by default. If the length after the while loop is still not the desired length, loop again.\n\nWhen the length matches the desired length, apply the callback or return the array.\n\nSee the [Demo](https://icyjoseph.github.io/async-ops/) and notice how in Asynchronous mode the clock keeps ticking!\n\n```js\nexport async function buildArrayAsync(length, elems, callback, timeout = 200) {\n  let index = 0;\n  let array = [];\n\n  async function concat() {\n    const startTime = now();\n\n    while (index \u003c length \u0026\u0026 now() - startTime \u003c= timeout) {\n      array.push(elems);\n      ++index;\n    }\n\n    if (index \u003c length) {\n      setTimeout(concat, 1);\n    } else if (index === length) {\n      return typeof callback === \"function\" ? callback(array) : array;\n    }\n  }\n\n  await concat();\n}\n```\n\n### Demo Setup\n\nA `React-App` with a `\u003cSwitch/\u003e` handling the sync/async mode of operation, a `\u003cClock/\u003e` and `\u003cCounter/\u003e`.\n\nA set of functions to build arrays in both synchronous and asynchronous modes.\n\n#### App\n\nThis React component controls the state of the Switch child, which is used to build in synchronous or asynchronous mode.\nThe Counter child receives the mode in the checked prop.\nThe Clock child handles its own state.\n\nThe Header component has no functional use for this demo.\n\n```jsx\nclass App extends Component {\n  state = {\n    value: TOGGLE_OFF,\n    checked: false\n  };\n\n  toggle = event =\u003e {\n    const target = event.target;\n    const value = target.type === CHECKBOX ? target.checked : target.value;\n    return this.setState({\n      value: toggleSwitch(value),\n      checked: target.checked\n    });\n  };\n\n  render() {\n    const { value, checked } = this.state;\n    return (\n      \u003cdiv className=\"App\"\u003e\n        \u003cHeader logo={logo} /\u003e\n        \u003cSwitch value={value} toggle={this.toggle} /\u003e\n        \u003cClock /\u003e\n        \u003cCounter checked={checked} /\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n### Clock\n\nUsing a custom component [`react-clock`](https://www.npmjs.com/package/react-clock), which in `1 second` intervals will update its own state and set a `new Date()`, thus making the clock tick.\n\n```jsx\nimport React, { Component } from \"react\";\nimport Clock from \"react-clock\";\nimport \"../App.css\";\n\nclass Timer extends Component {\n  state = {\n    date: new Date()\n  };\n\n  componentDidMount() {\n    this.interval = setInterval(\n      () =\u003e this.setState({ date: new Date() }),\n      1000\n    );\n  }\n\n  componentWillUnmount() {\n    clearInterval(this.interval);\n  }\n\n  render() {\n    const { date } = this.state;\n    return (\n      \u003cdiv className=\"Wrapper\"\u003e\n        \u003cClock value={date} /\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default Timer;\n```\n\n### Counter\n\nThis component uses changes in the `checked` props to update itself. It has the array, and measurements for `performance.now()` when the array building process is started by the user and for completion of the process.\n\nIt also has a `callback` method to update its state with an array and the `performance.now()` value when setting the array.\n\nIt also has two methods to build arrays, which are used depending on the value of checked.\n\nLastly, a reset method to set everything back to initial state.\n\n```jsx\nimport React, { Component } from \"react\";\nimport Common, { Button } from \"./Common\";\nimport {\n  largeNumber,\n  buildArraySync,\n  buildArrayAsync,\n  toSeconds\n} from \"../functions\";\nimport {\n  counterInitialState,\n  LENGTH_OF_ARRAY,\n  MAKE_LARGE_ARRAY,\n  RESET,\n  BUILD_TIME,\n  UNIT_SECONDS\n} from \"../constants\";\nimport \"../App.css\";\n\nexport class Counter extends Component {\n  state = {\n    ...counterInitialState // array:[], start:0, end:0\n  };\n\n  componentDidUpdate(nextProps) {\n    if (nextProps.checked !== this.props.checked) return this.reset();\n    else return null;\n  }\n\n  callback = start =\u003e array =\u003e {\n    return this.setState({ array, start, end: performance.now() });\n  };\n\n  makeArraySync = () =\u003e {\n    const start = performance.now();\n    const array = buildArraySync(largeNumber, 1);\n    return this.callback(start)(array);\n  };\n\n  makeArrayAsync = async () =\u003e {\n    const start = performance.now();\n    await buildArrayAsync(largeNumber, 1, this.callback(start), 100);\n  };\n\n  reset = () =\u003e\n    this.setState({\n      ...counterInitialState\n    });\n\n  render() {\n    const { start, end, array } = this.state;\n    const { checked } = this.props;\n    const buildTime = toSeconds(start, end);\n    return (\n      \u003cdiv className=\"Wrapper\"\u003e\n        \u003cCommon\u003e\n          \u003cCommon\u003e\n            {LENGTH_OF_ARRAY}\n            {array.length}\n          \u003c/Common\u003e\n          \u003cButton onClick={checked ? this.makeArrayAsync : this.makeArraySync}\u003e\n            {MAKE_LARGE_ARRAY}\n          \u003c/Button\u003e\n          \u003cCommon\u003e\n            {BUILD_TIME}\n            {buildTime}\n            {UNIT_SECONDS}\n          \u003c/Common\u003e\n          \u003cButton onClick={this.reset}\u003e{RESET}\u003c/Button\u003e\n        \u003c/Common\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default Counter;\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficyjoseph%2Fasync-ops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficyjoseph%2Fasync-ops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficyjoseph%2Fasync-ops/lists"}