Skip to content

Instantly share code, notes, and snippets.

@goldhand
Last active January 8, 2019 22:18
Show Gist options
  • Save goldhand/ae706adc5441fc87c5625f5c13a1190b to your computer and use it in GitHub Desktop.
Save goldhand/ae706adc5441fc87c5625f5c13a1190b to your computer and use it in GitHub Desktop.
Use a generator to make async stuff look sync.
/**
* Turns generators into async demons
*
* Within the generator function, any "yield" operator becomes enhanced with async powers
* allowing them to yield promises.
*
* @example
* spawn(function *() {
* const data = yield fetch('/foo.json'); // will be resolved and assigned to "data" var
* console.log(data); // write like its sync but its acually async ;)
* });
*
* @param {generator} generatorFn - generator function that will be wrapped
* @returns {Promise}
*/
export default function spawn(generatorFn) {
let generator = generatorFn();
let onFulfilled = arg => continuer('next', arg);
let onRejected = arg => continuer('throw', arg);
function continuer(verb, arg) {
let result;
try {
result = generator[verb](arg);
} catch (err) {
return Promise.reject(err);
}
if (result.done) {
return result.value;
} else {
return Promise.resolve(result.value).then(onFulfilled, onRejected);
}
}
return onFulfilled();
}
@goldhand
Copy link
Author

goldhand commented Feb 1, 2018

Explanation:

spawn(function *() {
  const data1 = yield fetch('./data1');
  const data2 = yield fetch('./data2');
});
  1. generator is created
  2. onFulfilled is invoked with no arg
  3. the first generator.next is invoked with no arg
    • when generator.next is invoked, yield fetch('./data1') is called
    • "result" is assigned the pending promise from fetch('./data1') (through generator.next)
    • the generator is paused at the first yield statement
  4. The pending promise is passed to Promise.resolve, which will wait to resolve
  5. Once resolved, the resolved value is passed to the continuer fn again with the resolved value as "arg"
    • The second pass through the continuer function begins.
  6. generator.next is invoked again, passing the previously resolved promise as the arg
    • the resolved promise is assigned to "data1"
    • generator resumes
    • fetch('./data2') is invoked
    • the unresolved promise from fetch('./data2') is assigned to "result"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment