EcmaScript Proposal - Promised functions

# Luiz Felipe Frazão Gonçalves (6 years ago)

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. :)

# Sebastian Malton (6 years ago)

An HTML attachment was scrubbed... URL: esdiscuss/attachments/20180412/4f6f02de/attachment-0001

# Tab Atkins Jr. (6 years ago)

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)); }

# Michał Wadas (6 years ago)

Is it codyfing of glorious The Deferred anti-pattern? petkaantonov/bluebird/wiki/Promise-Anti-patterns#the

# kai zhu (6 years ago)

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();
# Naveen Chawla (6 years ago)

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.

# Mike Samuel (6 years ago)

This seems like it could be done with decorators per tc39/proposal-decorators without introducing a new keyword.

@promises function sleep(...) { ... }

# Augusto Moura (6 years ago)

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;
}
# Isiah Meadows (6 years ago)

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

# kai zhu (6 years ago)

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"

# Jeremy Martin (6 years ago)

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).