Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
SynchronousAsync.js
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()
).then(
text => {
pending.delete(url);
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));
return user.name;
}
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));
@jaredpalmer

This comment has been minimized.

Copy link

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?
@sebmarkbage

This comment has been minimized.

Copy link
Owner Author

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.

@idibidiart

This comment has been minimized.

Copy link

idibidiart commented Dec 17, 2017

@jaredpalmer @sebmarkbage

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

https://gist.github.com/idibidiart/69fd5f9df339b5ef1783e6a8fae9fa51

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
You can’t perform that action at this time.