Skip to content

Instantly share code, notes, and snippets.

@KiaraGrouwstra
Last active January 3, 2021 15:36
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save KiaraGrouwstra/cab088c75f3cab131c3c41b97209fae0 to your computer and use it in GitHub Desktop.
Save KiaraGrouwstra/cab088c75f3cab131c3c41b97209fae0 to your computer and use it in GitHub Desktop.
using ES6 Proxy to let Promises/Observables pretend like they're regular values
// using ES6 Proxy to let Promises/Observables pretend like they're regular values.
// get the mapping function used for async objects
let getMapper = (target) => target instanceof Promise ? 'then' :
target instanceof Observable ? 'switchMap' : null;
// ^ fails if the Observable is in a local namespace e.g. Rx.Observable
// bind a value to its object if it's a function
let bindFn = (val, obj) => typeof val == 'function' ? val.bind(obj) : val;
// the proxy's trap handler
let handler = {};
// function invocation
handler.apply = (targetFn, that, args) => {
// must wrap Proxy target in functions for this `apply` trap :(
let target = targetFn();
let mapper = getMapper(target);
if(mapper) { // async
// transparently map; keep Proxy.
let val = target[mapper]((fn) => fn(args));
return new Proxy(() => val, handler);
} else { // sync
// no-op, ditch proxy
return target.apply(that, args);
}
};
// property access
handler.get = (targetFn, prop) => {
let target = targetFn();
let value;
let mapper = getMapper(target);
let bound = bindFn(target[prop], target);
if(mapper) { // async
if(prop in target) {
// ditch Proxy when directly invoking Promise/Observable method
return bound;
} else {
// transparently map; keep Proxy.
let value = target[mapper]((o) => bindFn(o[prop], o));
return new Proxy(() => value, handler);
}
} else {
// sync: no-op, ditch proxy
return bound;
}
};
let pretend = (v) => new Proxy(() => v, handler);
// now test our shiny `pretend` method.
let later = (v) => new Promise((resolve, reject) => setTimeout(() => resolve(v), 1000));
let obj = { greet: (name) => console.log('Hey ' + name) };
let prom = later(obj);
let greeter = pretend(prom);
// this is a Proxy'd Promise...
// yet we can call its methods using traditional syntax.
greeter.greet('you');
// ^ look mom, no `then`!
let res = greeter.then(() => console.log('sup'));
// un-Proxy by calling Promise methods like `then` (which `await` transpiles to AFAIK).
// This works similarly for Observables.
console.log('res', res);
// value: Promise<{ greet }>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment