Skip to content
Martin Carlberg edited this page Jan 18, 2023 · 8 revisions

Ways to use the latest Javascript(ECMAScript 2022) features with Cappuccino

Object Destructuring

I have been playing around with some new (for us) features in Javascript. It is very convenient to use Object Destructuring when working with CPPoint, CPRect and CPSize.

      let { x, y } = CPPointMake(12, 13)

This will assign x and y with 12 and 13.

The variables can have their own names too.

      let { x: myX, y: myY } = CPPointMake(12, 13)

This will assign myX and myY with 12 and 13.

This also works with more complex things like a CPRect.

      let { origin: { x, y }, size: { width, height } } = CPRectMake(20, 10, 100, 200)

The variables can be named here too.

      let { origin: { x: myX, y: myY }, size: { width: myWidth, height: myHeight } } = CPRectMake(20, 10, 100, 200)

Arrow Functions

The new Arrow Functions also make the code look a little bit cleaner.

    let array = [1,2,3,4,5,6,7,8,9]
    let result = 0
    [array enumerateObjectsUsingBlock:e => result += e]

This will add all numbers in the array and assign it to the variable result. The old code looked like this.

    let array = [1,2,3,4,5,6,7,8,9]
    let result = 0
    [array enumerateObjectsUsingBlock:function(e) { result += e }]

for (... of ...)

Another nice new feature is "for of". It works the way it should work and not like the old "for in"

let array = [1,2,3,4,5,6,7,8,9]
for (const element of array) {
  console.log(element) // prints the element
}

A nice feature of the "for of" is that it can use a custom iterator. This will allow us to use "for of" on CPDictionary and CPSet when we had a custom iterator made. This is not yet pushed to the branch. Something like this:

let dict = @{ @"a": 1, @"b": 2 }
for (const { key, value } of dict) {
  console.log(key + @": " + value)
}

This will output:

a: 1
b: 2

Async / Await

We don't have many asynchronous methods in the frameworks but there are some like this in CPURLConnection:

+ (CPURLConnection)sendAsynchronousRequest:(CPURLRequest)aRequest queue:(CPOperationQueue)aQueue completionHandler:(Function)aHandler;

We could add an async version of these methods without too much work needed. Something like this maybe...

+ (async JSObject /* { response: CPURLResponse, data: CPData, error: CPError } */)sendAsynchronousRequest:(CPURLRequest)aRequest queue:(CPOperationQueue)aQueue
{
    return new Promise(function(resolve, reject) {
        [[self alloc] _initWithRequest:aRequest queue:aQueue completionHandler:function(aResponse, aData, anError) {
            resolve( {response: aResponse, data: aData, error:anError })
        }];
    });
}

or an even simpler convenience method which defaults to the main queue. (This method is now added in the node branch)

+ (async JSObject /* { response: CPURLResponse, data: CPData, error: CPError } */)sendAsynchronousRequest:(CPURLRequest)aRequest
{
    return new Promise(function(resolve, reject) {
        [[self alloc] _initWithRequest:aRequest queue:[CPOperationQueue mainQueue] completionHandler:function(aResponse, aData, anError) {
            resolve( {response: aResponse, data: aData, error:anError })
        }];
    });
}

Now we can access this by just doing:

- (async @action)doAction:(id)sender {
    const { response, data, error } = await [CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:@"http://cappuccino.dev"]];
    if (error == nil) {
        let statusCode = [response statusCode];
        //do the stuff...
    } else {
        // Handle errors
    }
}