Tinkering with JavaScript again after some years of being blissfully ignorant, I notice that a style of programming which used to be popular there has caught on even more. These are "Promises"--which I've seen under other names like "Futures".
They've become so ingrained that they're now in the JavaScript language itself:
(Note: Worth pointing out is that there's a fundamental feature missing from the built-in promises, namely cancellation. Kind of makes you roll your eyes when you see someone deploy a major language feature to a worldwide audience--as if it's "the future of asynchronous programming and network requests"--when there's no way to cancel.)
When you have a promise in your hand, you can hook it up to clauses that will run if it succeeds, and clauses to run if it fails. You hook them together with successive function calls...where the THEN clauses are the successes, and the CATCH clauses are the failures:
some_promise.then(
function(val) {
log.insertAdjacentHTML('beforeend', val +
') Promise fulfilled (<small>Async code terminated</small>)<br/>');
}).catch(
// Log the rejection reason
(reason) => {
console.log('Handle rejected promise ('+reason+') here.');
});
You can make loooong chains out of these. Steps can be triggered by callbacks, or even by just an ordinary non-promise value falling through immediately from the previous step. It may help to look at a big example (from this article, if it looks weird with const
and =>
remember that JavaScript has changed a bit, and this stuff is in it now):
const wait = time => new Promise(
res => setTimeout(() => res(), time)
);
wait(200)
// onFulfilled() can return a new promise, `x`
.then(() => new Promise(res => res('foo')))
// the next promise will assume the state of `x`
.then(a => a)
// Above we returned the unwrapped value of `x`
// so `.then()` above returns a fulfilled promise
// with that value:
.then(b => console.log(b)) // 'foo'
// Note that `null` is a valid promise value:
.then(() => null)
.then(c => console.log(c)) // null
// The following error is not reported yet:
.then(() => {throw new Error('foo');})
// Instead, the returned promise is rejected
// with the error as the reason:
.then(
// Nothing is logged here due to the error above:
d => console.log(`d: ${ d }`),
// Now we handle the error (rejection reason)
e => console.log(e)) // [Error: foo]
// With the previous exception handled, we can continue:
.then(f => console.log(`f: ${ f }`)) // f: undefined
// The following doesn't log. e was already handled,
// so this handler doesn't get called:
.catch(e => console.log(e))
.then(() => { throw new Error('bar'); })
// When a promise is rejected, success handlers get skipped.
// Nothing logs here because of the 'bar' exception:
.then(g => console.log(`g: ${ g }`))
.catch(h => console.log(h)) // [Error: bar]
;
This resembles the kind of chaining done with enfix, so...
...might THEN and ELSE be promise-aware?
JavaScript uses the name THEN here to mean "do this thing on the right if the left happens" or "if the left is fulfilled". You can get a "false" result back from a promise:
var falsify = new Promise(
resolve => {
resolve(false) // calling resolve will fulfill the promise
}
);
falsify.then(x => console.log(x)) // this will print out false
Here we have to ask a question about what the rules are for the system. If you see something like:
all [...] then [print "done with all that stuff"]
print "moving on..."
Would you want the system to be based on a worldview where that could output:
"moving on..."
"done with all that stuff"
If you have a PROMISE! datatype in the picture, then programs which use them will become asynchronous even if they don't appear to be written in an asynchronous style. The more automatic it is, the greater the risks become.
JavaScript's promises involve very little system support. Not only were most promise library designs done before the language supported them at all, one of the most popular implementations doesn't even use the new JS built-in promises...but cooks up its own using functions and objects.
So this is different from what Gabriele was suggesting when he brought up promises as a likely path forward for PORT!s in his Topaz design notes. He's suggesting that a PROMISE! would be an active datatype, where if you merely said data: read url and got a promise back, then length of data, it would automatically wait until the length was known. You wouldn't have to explicitly WAIT on it (or have a THEN, as suggested above).
It's trying to suggest a synchronous client of READ could go on their merry way using it just as if they got bytes back, or tactically drill in with promise-aware constructs on an as-needed basis. As nice as that sounds it's not realistic. What you'd need to accomplish such a goal more or less asks the runtime support for lazy evaluation, and the restrictions that brings in would push back to where the user's style of programming would have to morph into something much more like Haskell than like Rebol/JavaScript/etc.
JavaScript's answer is to quarantine down the promise-aware constructs of the language to more or less zero. Their if() treats them as truthy, not "wait on them asynchronously and see if the result was truthy, or falsey if an error". Things like .then() are methods on the promise itself, not generic language operators.
As I've pointed out that people aren't even necessarily with the built-in promises of JavaScript--enough to continue using libraries that cobble the protocols together out of objects and functions--it may suggest that zero is not a good sweet spot for language support. To really be compelling enough for pervasive use there may need to be some special sauce you don't get elsewhere (including, I'd have to say, cancellation).
I do wonder if conditionals could be a place where it might be good to synchronize a promise. Or if ANY and ALL could imply WAITs. But certainly it seems like it would be appealing if THEN and ELSE could be used...so I guess that's what I'll put out there for a starting thought.