Skip to main content

News

Topic: ES6: Promises (Read 4800 times) previous topic - next topic

0 Members and 1 Guest are viewing this topic.
  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
ES6: Promises
Just found out about these.  Promises are neat, if somewhat difficult to wrap your mind around at first.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

You create a Promise, which registers a callback or three to resolve the promise at the appropriate time.  When the promise is eventually resolved, the function passed to .then() is called.  Essentially it's a glorified one-off delegate, nothing too impressive.  But unlike traditional delegates, you can chain .then() clauses.  This allows you to write asynchronous code that reads like a normal sequential operation.  And unlike delegates, there's no chance of a race condition: if the operation happens to complete before a handler can be attached, the call will be deferred until one is.

Oh, and I've already written a polyfill for it. :)

neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: ES6: Promises
Reply #1
Oh, promises, I like them! I've used promises a lot at work. Every angular app I've made and every nodejs server I worked on is entirely promises. It's weird this is ES6 when it's not out yet but I've used them so much. :P
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #2
I think I want to update Scenario to use these.  Scenario:run() would return a promise that is resolved when the scene completes.  I think that would be useful.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: ES6: Promises
Reply #3
This, along with some of the new parallel extensions that were proposed in ES5 and ES6 are what makes me think we don't need to worry about making the whole Sphere API callback based, as was originally proposed in Pegasus. JS is gaining language level concurrency and, if not actual thread-awareness, primitives that let you do what thread-awareness would.

This is what Haskell functors (real functors, not what C++ calls a functor) look like, I think. I haven't really hit a point where they seemed like the perfect tool for the job yet, though.


Oh, and I've already written a polyfill for it. :)


Not that long ago, I read on Chad Austin's blog that this was impossible. Did the spec change, is it only mostly a polyfill, or do you reckon Chad Austin is wrong?

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #4
It's certainly not impossible.  Maybe there are parts of the spec that have to be done natively (I haven't come across any yet), but for the most part it's just a glorified callback wrapper.  Trivial to implement in script, at least once you wrap your head around the logic behind them.  The tricky part to get right, it seems, is the chaining.  Every then has to return a promise of its own, which is contingent on the previous one in the chain.  It's easy to screw it up.

See here for a tutorial to create a working JS implementation:
http://www.mattgreer.org/articles/promises-in-wicked-detail/#chaining-promises

Honestly what confuses me the most about the chaining is the idea of promises making promises of their own (then returns a promise).  It's like some sort of paradox that, every time I try to reason about it it just completely baffles me.
  • Last Edit: May 09, 2015, 11:41:12 pm by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: ES6: Promises
Reply #5
Are you going to add the done() method to get meaningful error messages?
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #6
Most likelyYes, I already did it, it definitely seems useful.

Edit: My quick-and-dirty but very readable polyfill, based on the above mentioned tutorial:
Code: (javascript) [Select]
/**
* minisphere Runtime 1.1b4 - (c) 2015 Fat Cerberus
* A set of system scripts providing advanced, high-level functionality not
* available in the engine itself.
*
* [mini/Promise.js]
* A polyfill for the ES6 Promise object.
**/

Promise = (function(undefined)
{
'use strict';

function Promise(fn)
{
var deferred = [];
var state = 'pending';
var result = undefined;

function handle(handler)
{
if (state == 'pending')
deferred.push(handler);
else {
var callback = state == 'resolved' ? handler.fulfiller
: state == 'rejected' ? handler.rejector
: undefined;
if (typeof callback !== 'function') {
if (state == 'resolved') handler.resolve(result);
if (state == 'rejected') handler.reject(result);
} else if (state == 'rejected') {
handler.reject(callback(result));
} else {
try {
handler.resolve(callback(result));
} catch(e) {
handler.reject(e);
}
}
}
}

function resolve(value)
{
if (state != 'pending') return;
try {
if (value && typeof value.then === 'function') {
value.then(resolve, reject);
return;
}
state = 'resolved';
result = value;
for (var i = 0; i < deferred.length; ++i)
handle(deferred[i]);
deferred = [];
} catch(e) {
reject(e);
}
}

function reject(reason)
{
if (state != 'pending') return;
state = 'rejected'
result = reason;
for (var i = 0; i < deferred.length; ++i)
handle(deferred[i]);
deferred = [];
}

this.catch = function(errback)
{
return this.then(undefined, errback);
};

this.then = function(callback, errback)
{
return new Promise(function(resolve, reject) {
handle({
resolve: resolve, reject: reject,
fulfiller: callback,
rejector: errback
});
});
};

this.done = function(callback, errback)
{
var self = arguments.length > 0 ? this.then.apply(this, arguments) : this;
if (typeof errback !== 'function')
self.catch(function(reason) { throw reason; });
};

fn(resolve, reject);
};

Promise.all = function(iterable)
{
return new Promise(function(resolve, reject) {
var promises = [];
var values = [];
var numPromises = iterable.length;
for (var i = 0; i < numPromises; ++i) {
var v = iterable[i];
if (!v || typeof v.then !== 'function')
v = Promise.resolve(v);
promises.push(v);
v.then(function(value) {
values.push(value);
if (values.length == numPromises)
resolve(values);
}, function(reason) {
reject(reason);
});
}
});
};

Promise.race = function(iterable)
{
return new Promise(function(resolve, reject) {
var numPromises = iterable.length;
for (var i = 0; i < numPromises; ++i) {
var v = iterable[i];
if (!v || typeof v.then !== 'function')
v = Promise.resolve(v);
v.then(function(value) { resolve(value); },
function(reason) { reject(reason); });
}
});
};

Promise.reject = function(reason)
{
return new Promise(function(resolve, reject) {
reject(reason);
});
}

Promise.resolve = function(value)
{
return new Promise(function(resolve, reject) {
resolve(value);
});
}

return Promise;
})();



Edit 2: And me screwing around with it. :P  This is the kind of thing I do to make sure everything works, and the underlying reason for minisphere's high level of compatibility:
Code: (javascript) [Select]
	new Promise(function(resolve, reject) {
resolve("the pig ate everything at 8:12");
}).then(function(value) {
Print(value);
return new Promise(function(resolve, reject) {
resolve("the cow ate the pig");
});
}).then(function(value) {
Print(value);
throw new Error("the ape ate the cow");
}).done(function(value) {
Print(value);
}, function(reason) {
Print(reason);
});


Code: [Select]
Output:
the pig ate everything at 8:12
the cow ate the pig
Error: the ape ate the cow
  • Last Edit: May 10, 2015, 02:41:40 am by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #7
Code: (javascript) [Select]
// EmptyPromise constructor
// An EmptyPromise will never be fulfilled or rejected; it will remain
// pending forever. Kind of depressing symbolism, huh? :o)
function EmptyPromise() {}
EmptyPromise.prototype = new Promise(function() {});
EmptyPromise.prototype.toString = function() { return "[empty promise]"; };


Can you tell I'm having tons of fun with these yet? :o)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: ES6: Promises
Reply #8
For a good example of how they are used all the time on the internet, here is a stack-overflow question that succinctly demonstrates the importance of promises in http contexts: Here.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #9
Yeah, I kind of stumbled my way into understanding promises as I was attempting to implement them (how I come to understand most things).  I now have a fully-functional implementation as part of the minisphere runtime, and for practice I reimplemented Scenario using promises.  So far it's working pretty well!
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: ES6: Promises
Reply #10
I do have to add though, in a way I kind of feel like promises are a hack.  I tend to think this from time to time about a lot of JS stuff, but especially so here.  Promises, while useful, strike me as basically a cheesy replacement for worker threads in a language that, in this modern day and age of quad-, hex- and even octo-core processors, still doesn't have true multithreading.  Oh, sure, you can make a browser or other environment with multiple threads (or even multiple processes in Chrome's case), but each thread has to get its own entirely separate GC context because no JS engine in existence is thread-safe.  It's a shame, JS is such an awesome (and fun!) language to code in, but it's hamstrung at times by the lack of threads.

For ES7, I think everyone would be better served trying to standardize some sort of threading framework instead of adding a bunch of bells and whistles like ES6 is doing.  JS is going to start feeling like a relic if it stays single-threaded for much longer.  That's how I feel, anyway.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: ES6: Promises
Reply #11
I understand. Node.js was set up to use promises for Async IO and since it's based on this, it's doomed to fail. Programming languages like Go are much better suited to asynchronous web server handling that a JS based one. But I think as for how JS goes, the API of promises is rather nice.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here