npm package: @realdennis/next-frame
Description: await until next frame
Author: realdennis
Latest version: 1.0.0

<span class="badge-github-actions"><a href="https://github.com/realdennis/nextFrame/actions" title="View this project on GitHub"><img src="https://github.com/realdennis/nextFrame/workflows/Node%20CI/badge.svg" alt="GitHub Actions" /></a></span>
<span class="badge-npmdownloads"><a href="https://npmjs.org/package/@realdennis/next-frame" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/@realdennis/next-frame.svg" alt="NPM downloads" /></a></span>

# nextFrame

Some utils about promisify of `window.requestAnimationFrame`, you can use this in your async function to slice the long callback bottleneck to improve performance and avoid frame drop in your callback.

## Why use nextFrame instead of requestAnimationFrame?

Since requestAnimationFrame is a callback usage, if we want to deal with complexity flow, the callback hell would be crap.

## Installation

```bash
$ npm install @realdennis/next-frame
```

## Usage

It expose three methods `nextFrame` / `perFrameReduccer` / `perFrameMapper` for difference purpose.
It expose one factory function `nextFrameFactory` to generate the `nextFrame` and `cancelNextFrame` pair.

```javascript
// ESModule
import {
  nextFrame,
  nextFrameFactory,
  perFrameReduccer,
  perFrameMapper
} from "https://unpkg.com/@realdennis/next-frame/dist/index.mjs";
```

### nextFrame

```javascript
import { nextFrame } from '@realdennis/next-frame';

window.onclick = async ()=>{
    await nextFrame()
    /**
     * ... something click callback step1
     */
    chunk1();
    await nextFrame()
    /**
     * ... something click callback step2
     */
    chunk2();
    await nextFrame()
    /**
     * ... something click callback step3
     */
    chunk3()
}
```
These 3 chunk will be execute in 3 serial different frame, and share the same closure variable, race-safe.

### nextFrameFactory

Here is a factory to generate a cancelable `nextFrame`, by the pair of `cancelNextFrame`. They are paired, once you call `cancelNextFrame`, the `nextFrame` generated by the same factory call WILL NOT WORK ANYMORE.

It will help us to **cancel the task was scheduled to the long future**.

Both resolve and reject of the `nextFrame` promise will passing the `currentFrame` in, so you can know the task progress.

```javascript
const { nextFrame, cancelNextFrame } = nextFrameFactory();
const aLargeTaskCallback = async ()=>{
    try{
        console.log(1); // Large Task 1
        var { currentFrame } = await nextFrame();
        console.log(2); // Large Task 2
        await nextFrame();
        // ...same
        console.log(33); // Large Task 33
        await nextFrame();
        console.log(34); // Large Task 34
    }catch(e){
        console.log("Get the cancel signal");
        console.log("current task progress...", e.currentFrame);
    }
}

// In other place, condition trigger
useEffect(() => {
    aLargeTaskCallback()
    return () => cancelNextFrame();
},[])
// Some kind like this
// component is unmounted, and we want to clear the task we schedule in the future
```


### perFrameReducer

It provides `perFrameReducer`, an array reduce callback, reduce the callback array to promise, and schedule it all to different frame like above.

```javascript
import { perFrameReducer } from '@realdennis/next-frame';

window.onclick = async ()=>{
    await [chunk1,chunk2,chunk3].reduce(perFrameReducer)
    console.log('Done')
}
```

### perFrameMapper

It provides `perFrameMapper`, an array map callback, map the callback array to promised callback array, and each promised callback has itself start frame.

```javascript
import { perFrameMapper } from '@realdennis/next-frame';

window.onclick = async (){
    const scheduledChunks =  [chunk1,chunk2,chunk3].map(perFrameMapper);
    //[chunk1InFrame1func , chunk2InFrame2func, chunk3InFrame3func]
    Promise.all(scheduledChunks.map(cb=>cb()))
        .then(console.log('concurrent done'));
}
```

## License

LICENSE MIT © 2019 realdennis