EcmaScript Proposal - Promised functions
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20180412/4f6f02de/attachment-0001
On Thu, Apr 12, 2018 at 9:07 AM, Luiz Felipe Frazão Gonçalves <luizfelipefrazaogoncalves at gmail.com> wrote:
One new proposal for EcmaScript.
Promised Functions
Like async/await, the promised functions would be preceded by a keyword. In the case, promised, it would change the default behavior of the function, making it behave as a promise.
I will use as an example a classic sleep function:
function sleep(forHowLong) { return new Promise((resolve, reject) => { setTimeout(function() { resolve();
/** * For reject: * * reject(Error('Some error')); */ }, forHowLong);
}); }
I think to avoid the huge amount of callbacks, there should be a syntax similar to this:
Some of these callbacks aren't necessary:
function sleep(forHowLong) { return new Promise(resolve=>setTimeout(resolve, forHowLong)); }
Tho even if you do use all the same callbacks, just formatting it properly helps:
function sleep(forHowLong) { return new Promise(resolve=>setTimeout(_=>resolve(), forHowLong)); }
Is it codyfing of glorious The Deferred anti-pattern? petkaantonov/bluebird/wiki/Promise-Anti-patterns#the
or ... you could bypass these low-level promise-abstractions altogether, and elegantly solve the actual integration-level task asked by your pm - with a single, easy-to-debug, throwaway recursive-callback (which will likely work exactly the same 10 years from now, while god-knows how the magic will evolve with promises in that time).
/*
* example.js
*
* example usage:
* $ FOR_HOW_LONG=1000 node example.js
*
* example output:
* 0 ms - start recusive-callback
* 1005 ms - waited for server and db to initialize
* 1007 ms - read file example.js which had 2519 characters
* 1844 ms - fetched 1270 bytes of data from https://www.example.com
* 1844 ms - finished recursive-callback
*/
/*jslint
bitwise: true,
browser: true,
maxerr: 4,
maxlen: 100,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
'use strict';
var modeNext, onNext, timeStart, tmp;
onNext = function (error, data) {
// For reject:
if (error) {
console.error(error);
return;
}
modeNext += 1;
switch (modeNext) {
case 1:
timeStart = Date.now();
console.log((Date.now() - timeStart) + ' ms - ' +
'start recusive-callback');
// sleep
setTimeout(onNext, process.env.FOR_HOW_LONG);
break;
case 2:
console.log((Date.now() - timeStart) + ' ms - ' +
'waited for server and db to initialize');
onNext();
break;
case 3:
// read this file
require('fs').readFile(__filename, 'utf8', onNext);
break;
case 4:
console.log((Date.now() - timeStart) + ' ms - ' +
'read file ' + __filename + ' which had ' + data.length + ' characters');
onNext();
break;
case 5:
// fetch data from https://www.example.js
require('https').request(
require('url').parse('https://www.example.com'),
function (response) {
onNext(null, response);
}
)
// handle http-request error
.on('error', onNext)
.end();
break;
case 6:
// handle http-response error
data.on('error', onNext);
// collect http-response chunks
tmp = [];
data.on('data', function (chunk) {
tmp.push(chunk);
});
// concatenate http-response chunks
data.on('end', function () {
onNext(null, Buffer.concat(tmp));
});
break;
case 7:
console.log((Date.now() - timeStart) + ' ms - ' +
'fetched ' + data.length + ' bytes of data from https://www.example.com');
onNext();
break;
case 8:
console.log((Date.now() - timeStart) + ' ms - ' +
'finished recursive-callback');
break;
}
};
modeNext = 0;
onNext();
I'd prefer a "new Promise" literal analogous to {} for objects and '' for strings:
p\
resolve=>
setTimeout(
resolve,
forHowLong)
\
I think the "this.resolve" proposal wouldn't work for methods, for example.
This seems like it could be done with decorators per tc39/proposal-decorators without introducing a new keyword.
@promises function sleep(...) { ... }
Also it can be already be implemented in user land with high orders functions:
const sleep = promised(function (time) {
window.setTimeout(() => this.resolve(), time);
});
A simple implementation of a promised
helper
const promised = (fn) => (...args) => {
let target;
const promise = new Promise((resolve, reject) => {
target = { resolve, reject };
});
fn.apply(target, args);
return promise;
}
This is already basically possible in userland, so to me, syntax seems wholly unnecessary.
(re: Augusto)
Your helper could be simplified further:
function promised(fn) {
return (...args) => new Promise((resolve, reject) => {
Reflect.apply(fn, {resolve, reject}, args)
})
}
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
On 13 Apr 2018, at 3:48 AM, Mike Samuel <mikesamuel at gmail.com> wrote:
This seems like it could be done with decorators per tc39/proposal-decorators, tc39/proposal-decorators without introducing a new keyword.
@promises function sleep(...) { ... }
the problem with using decorators, object.defineproperty, and other meta-programming features in javascript, is that no one has carefully thought out how they can be easily debugged, especially in javascript’s quirky, non-blocking-design context.
imagine google’s gwt, and removing most of its compile-time checks. that's essentially the direction es6/es7/es8/etc seems to be headed (and if that’s the case, how is it better than gwt?). is this what we want? a language with java's meta-programming power, but lacking most of its safety-features?
how successful in getting shipped, do you think a java-project as complicated as a typical web-project would be without compile-time meta-programming checks? exactly ... and this is pretty much what the current state of the web-industry is like :`(
to illustrate with code, npm has this particular meta-programming gem [1], which leads to this surprising behavior (and corresponding real-world scenario where this was encountered [2]):
/*
* example1.js
*
* example usage:
* $ npm install npm && node example1.js
*
* example output:
* Error: Call npm.load(config, cb) before using this command.
* See the README.md or bin/npm-cli.js for example usage.
*/
/*jslint
node: true
*/
'use strict';
var command, npm;
npm = require('npm');
Object.keys(npm.commands).forEach(function (key) {
// how many people would expect this to throw an error?
command = npm.commands[key];
// rather than this?
try {
command();
} catch (ignore) {
}
});
console.log('caught all errors');
here's the fix that was applied (and corresponding real-world solution [3]). but again, why should web-developers have to second-guess that arbitrary property-accesses might throw errors (and waste valuable engineering-time constantly guarding against them)?
/*
* example2.js
*
* example usage:
* $ npm install npm && node example2.js
*
* example output:
* caught all errors
*/
/*jslint
node: true
*/
'use strict';
var command, npm, objectKeysSafe;
npm = require('npm');
objectKeysSafe = function (dict) {
/*
* this function will return a list of the dict's keys,
* that are safely accessible
*/
return Object.keys(dict).filter(function (key) {
try {
return dict[key] || true;
} catch (ignore) {
}
});
};
objectKeysSafe(npm.commands).forEach(function (key) {
// how many people would expect this to throw an error?
command = npm.commands[key];
// rather than this?
try {
command();
} catch (ignore) {
}
});
console.log('caught all errors');
[1] npm/npm/blob/v5.8.0/lib/npm.js#L112, npm/npm/blob/v5.8.0/lib/npm.js#L112 - "npm/npm.js at v5.8.0 · npm/npm" [2] travis-ci.org/npmdoc/node-npmdoc-npm/builds/365262668#L1300, travis-ci.org/npmdoc/node-npmdoc-npm/builds/365262668#L1300 - "Travis CI - Test and Deploy Your Code with Confidence" [3] kaizhu256/node-apidoc-lite/blob/2017.4.12/lib.apidoc.js#L1019, kaizhu256/node-apidoc-lite/blob/2017.4.12/lib.apidoc.js#L1019 - “node-apidoc-lite/lib.apidoc.js at 2017.4.12"
the problem with using decorators, object.defineproperty, and other meta-programming features in javascript, is that no one has carefully thought out how they can be easily debugged, especially in javascript’s quirky, non-blocking-design context.
That's a rather broad statement spanning many language features, and further speaks to a predominately implementation-level concern (runtime debugging tools), as opposed to explaining what about the specification prohibits what you're looking for.
Regarding some of the existing suggestions from the group, while I'm not especially familiar with compile-time type checkers for JavaScript, a quick search seems to suggest that at least TypeScript supports both type-safe decorators and type-safe higher order functions, which both seem well-suited as userland solutions to this.
FWIW, I can appreciate the ergonomics of what you're advocating for here, but please keep in mind that the implementation and specification barriers for new syntax is much higher than for other enhancements to the language, and I'd like to friendly suggest that this may not satisfy that threshold. You would likely see a warmer response if you can demonstrate some community demand and/or adoption of a babel plugin that implements this feature (although, personally, I would suggest just rolling with a userland approach).
One new proposal for EcmaScript.
Promised Functions
Like
async/await
, the promised functions would be preceded by a keyword. In the case,promised
, it would change the default behavior of the function, making it behave as a promise.I will use as an example a classic
sleep
function:function sleep(forHowLong) { return new Promise((resolve, reject) => { setTimeout(function() { resolve(); /** * For reject: * * reject(Error('Some error')); */ }, forHowLong); }); }
I think to avoid the huge amount of callbacks, there should be a syntax similar to this:
promised function sleep(forHowLong) { setTimeout(function() { this.resolve(); // could even create a keyword like "resolve" /** * For reject: * * this.reject(Error('Some error')); */ }, forHowLong); }
Note that the hypothetical keyword "promised" before the function statement makes it act as a promise.
Just a crazy idea I had. :)