0

Consider following case:

var Calc = function () {
   // proprties
   this.value = 0

   // handler's
   this.Add = (__value) => { this.value = this.value + __value; return this }
   this.Subtract = (__value) => { this.value = this.value - __value; return this }
}

var = (new Calc()).Add(2).Subtract(1) // console.log() => 1

but if you wrap Object in async await something like

var Calc = async function () {
   // proprties
   this.value = 0

   // handler's
   this.Add = async (__value) => { this.value = this.value + __value; return this }
   this.Subtract = async (__value) => { this.value = this.value - __value; return this }
}

(await new Calc()).Add(2).Subtract(1) // console.log() => undefined
(await (await new Calc()).Add(2)).Subtract(1) // console.log() => 1

I know the reason as Promise is returned it need to be resolved for that you just wrap your code inside () and once statement is executed you can continue chain.

What i am looking for.

await newCalc().Add(2).Subtract(1) // console.log() => 1
7
  • As very first comment: why is your Calc not a class? Commented Oct 11, 2019 at 19:47
  • 3
    those operations are not async Commented Oct 11, 2019 at 19:49
  • you can't use top level await yet Commented Oct 11, 2019 at 19:50
  • Just as a general question, is there any particular reason you're using Promises for a Calculator? You can if you're getting numbers through network requests, but if you're just calculating numbers there doesn't seem to be any reason for it. Commented Oct 11, 2019 at 19:52
  • 2
    Being blunt here: just putting async in front of anything does not make sense. Even if those ops Add and Subtract async why does the Calc function is async. And double await why? what is it that u need to accomplish here. Commented Oct 11, 2019 at 19:55

1 Answer 1

2

With the caveat that await can only be used inside an async function, the kind of API you want is possible, it's just a bit more complicated.

Plenty of libraries such as knex, jQuery and nightmare.js implement chaining to compose asynchronous operation. But the chainable methods are not asynchronous. Instead, the asynchronous operation is only carried out at the end of operations (when you want the result) but the methods themselves are synchronous. In the case of knex for example, the asynchronous operation is only carried out when .then() is called.

Here's one way you can do it:

function Calc () {
    this.operations = [];
    this.value = 0;
}

Calc.prototype = {
    add: function (x) {
        // schedule a function that performs async addition:
        this.operations.push(() => {
            return new Promise(ok => {
                ok(this.value + x);
            });
        });
        return this;
    },
    subtract: function (x) {
        // schedule a function that performs async subtraction:
        this.operations.push(() => {
            return new Promise(ok => {
                ok(this.value - x);
            });
        });
        return this;
    },
    // THIS IS WHERE WE DO OUR MAGIC
    then: async function (callback) {
        // This is finally where we can execute all our
        // scheduled async operations:
        this.value = 0;
        for (let i=0; i<this.operations.length; i++) {
            this.value = await operations[i]();
        }
        return callback(this.value); // since the await keyword will call our
                                     // then method it is the responsibility of
                                     // this method to return the value.
    }
}

Now you can use it like this:

async function main () {
    let x = await new Calc().add(2).subtract(1);
    console.log(x);
}
main();

Note that the code above is functionally equivalent to:

new Calc().add(2).subtract(1).then(x => console.log(x));
Sign up to request clarification or add additional context in comments.

4 Comments

Exactly the same that I started to write... Nice code & explanation, +1
Also, don't forget to empty the operations array when then is called, and all operations done
@FZs It depends on what you want to do. Knex for example doesn't empty the operations array (or whatever it uses internally for it) so that you can reuse your query object to fetch the data again from the db
I agree, but in this context, keeping it will result in some weird behavior. Just try when the Add and Subtract modifies c: const c=new Calc();c.Add(2).Subtract(1);c.then();c.then();c.then(()=>console.log(c.value)), it will log 3!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.