Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
let cache = new Map();
let pending = new Map();
function fetchTextSync(url) {
if (cache.has(url)) {
return cache.get(url);
if (pending.has(url)) {
throw pending.get(url);
let promise = fetch(url).then(
response => response.text()
text => {
cache.set(url, text);
pending.set(url, promise);
throw promise;
async function runPureTask(task) {
for (;;) {
try {
return task();
} catch (x) {
if (x instanceof Promise) {
await x;
} else {
throw x;
function getUserName(id) {
var user = JSON.parse(fetchTextSync('/users/' + id));
function getGreeting(name) {
if (name === 'Seb') {
return 'Hey';
return fetchTextSync('/greeting');
function getMessage() {
let name = getUserName(123);
return getGreeting(name) + ', ' + name + '!';
runPureTask(getMessage).then(message => console.log(message));

This comment has been minimized.

Copy link

@jaredpalmer jaredpalmer commented Dec 15, 2017

This is awesome. Couple of questions:

  • Why does runPureTask() on line 18 have .then after it? Isn't the point to be able to call it all synchronously?
  • What are perf implications in major browsers on relying on throwing the exception?

This comment has been minimized.

Copy link
Owner Author

@sebmarkbage sebmarkbage commented Dec 16, 2017

@jaredpalmer The point is not for the whole program and event loop to be synchronous. Just a small part of it. At some point you have to have a coordinator that says what happens to the rest of the program while you're blocked.

Note that this is not throwing an Error. Creating an error object can some times be a bit more expensive since that stores the stack trace. Throwing is not necessarily zero cost, but it's minor compared to the I/O that is happening here.

This thing is very much optimizing for most things to not block on I/O and is cached. The major downside is not the throw, it is that you may have to reuse some work if it does. If you're expecting to mostly hit I/O in many places I'd recommend async/await instead.


This comment has been minimized.

Copy link

@idibidiart idibidiart commented Dec 17, 2017

@jaredpalmer @sebmarkbage

I added promise rejection scenario (see "try this" comment) and composition of fetchTextSync:

I like how I can compose idiomatically with this, e.g.: let result = fetchSync('/test/1a') + ' ' + fetchSync('/test/1b')

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