Skip to content
This repository has been archived by the owner on Aug 29, 2020. It is now read-only.

dead-claudia/proposal-parallel-generator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 

Repository files navigation

Parallel generators

There should be a syntax function **gen() { ... } to create a parallel generator where you can resume from the same state multiple times. The first call would return an iterator result with an undefined value and done: false, starting at the first value. Each parallel iterator result contains the usual done and value properties, but also a .next(value) method to move to the next iteration and get the next state, a .throw(value) which works similarly, and a .return(value) that works similarly. Each result would also be iterable, where you can iterate to the last result.

Syntactically, it'd otherwise look like generators, aside from the extra asterisk after function, and the only runtime difference is that when you call .next() while it's still running, it just resumes from the last state. (This is the case for both sync and async generators.)

Why?

Generators are really coroutines, and coroutines are really convenient for certain types of state machines. But sometimes, it's easier to model a state machine as a decision tree of sorts. One concrete example of this is user input - it's often much easier to model it as a user storyline rather than a state machine with a bunch of complex inputs.

Example

Here's an example of how this might be used in practice, adapted from Redux Saga's readme:

import Api from "..."

/*
 * Starts fetchUser on each dispatched `FETCH_USER` action.
 * This can happen concurrently, too.
 */
async function **reducer(model) {
    while (true) {
        const {type, ...action} = yield

        if (type === "FETCH_USER") {
            try {
                const user = await Api.fetchUser(action.userId)
                model.update({type: "USER_FETCH_SUCCEEDED", user: user})
            } catch (e) {
                model.update({type: "USER_FETCH_FAILED", message: e.message})
            }
        }
    }
}

export default reducer

The view might do something like this:

import React, {useContext} from "react"

function View({userId}) {
    const reducer = useContext(Reducer)

    useEffect(() => {
        reducer.next({type: "FETCH_USER", userId})
    }, [userId])

    // render view
}

What happens if .throw or .return is called while it executes?

Existing pending blocks will continue until they either return, throw, or yield, then in case of yield, it returns {done: true, value: arg} for .return(arg) or throws arg for .throw(arg). That way, it stays consistent and has a defined pathway even if things go wonky, and the generator is able to clean up after itself. It may be appropriate to also integrate with cancellation somehow.

About

(ABANDONED) A proposal to for a type of generator that can be resumed from the same state while it's running

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published