Skip to main content

News

  • The forums have now switched to ElkArte, an SMF fork which is more modern and maintained better!

Topic: Future of Sphere: Using Async and Promises (Read 846 times) previous topic - next topic

  • Rahkiin
  • [*]
  • Verified
Future of Sphere: Using Async and Promises
I split this off from my response on the Plugins part.

Let's make Pegasus async (non-blocking). Node is, indeed, and we should follow this path. No multi-threading (for now), but one big event-loop. Yes, I say event-loop because it sounds a lot like 'game-loop'. (node uses an event-loop).
This might be harder than non blocking without an event loop, but it does solve a lot of issues regarding files and such. I am not sure how this would be designed, especially in a game, but I will look into it today.

If we follow the node.js modules+async path, I think I could implement a full-software (slow) engine by implementing Pegasus on top of Node. This could be used for prototyping. Not really for games, that is too slow. Also, 'npm install sphere-pegasus'.

If we go with Async, I suggest we use Promises. Promises is an ES6 builtin and there is Bluebird and many other libraries for ES5. You can also implement it yourself if you like, which is good fun. Use promises where-ever you can, that is.
As you might have no idea what promises are, I will explain it a bit:

A promise is a promised value. A promise can either reject (with an error) or resolve (with a value).
A quick example: loading a file. That can either fail (file did not exist, system error, whatever) or it can succeed (with the data). Now, in Node-style-async you would thus have the following function:

Code: (javascript) [Select]
function loadFile(name: string, callback: (error: Error, result: any) => void): void
(Yes, this is JavaScript with typings so that is easy)
You call this:
Code: (javascript) [Select]
loadFile("myFileName.y", function (error, result) {
   if (error) {
      console.log("Oh no! An error occurred! EXIT!");
      process.exit(1);
   }

    console.log("All went well, lets print and exit.", result);
    process.exit(0);
});


This ain't too bad but it is not nice either. Lets say we want to read a file and then directly write it.
Code: (javascript) [Select]
loadFile("myFileName.y", function (error, result) {
   if (error) {
      console.log("Oh no! An error occurred! EXIT!");
      process.exit(1);
   }

   console.log("All went well, lets print and write.", result);

   writeFile("myOtherFileName.y", result, function (error) {
        if (error) {
            console.log("Oh No!: Could not write! EXIT!);
            process.exit(1);
        }
        console.log("ALl went well, wrote the file. Bye.");
        process.exit(0);
   });
});

This is getting worse and worse.

Also, you are processing errors over and over again.
Lets see what it all looks like using a Promise: a value that promises a result.
Code: (javascript) [Select]
 function loadFile(name: string): Promise


Code: (javascript) [Select]

loadFile("myFileName.y")
   .then(function (result) {
       return writeFile("myOtherFileName.y", result);
   })
   .then(function () {
      console.log("All Finished!");
   })
   .catch(function (error) {
      console.log("An error occurred:", error);
      process.exit(1);
   });

Short and nice! WIth ES6/TypeScript this is even shorter (you can compile ES6/TS to ES5):

Code: (javascript) [Select]

loadFile("myFileName.y")
   .then((result) => writeFile("myOtherFileName.y", result))
   .then(() => console.log("All Finished!"))
   .catch((error) => {
      console.log("An error occurred:", error);
      process.exit(1);
   });

That is beautiful! What you read is what happens! And no deep tabs.

I hope I convinced you that this is the way to go.

(You might wonder, what does loadFile code looks like?)
Code: (javascript) [Select]

function loadFile(name: string): Promise {
    return new Promise(reject: Function, resolve: Function) {
        // Do stuff
        if (an Erorr happened) {
           reject(new Error("An error"));
        } else {
            resolve("data");
        }
    }); // returns a promise right-away
}


For simple function you can also return Promise.resolve() or Promise.reject(). So a stub implementation that always resolves:
Code: (javascript) [Select]

function loadFile(name: string): Promise {
    return Promise.resolve("somedata"); // STUB
}


If you have any questions of anything, respond!

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Future of Sphere: Using Async and Promises
Reply #1
minisphere 3.0 includes basic async support.  There are no async functions in the core API -- it basically mimics Sphere 1.5 with a bunch of backwards-compatible enhancements.  minisphere 4.0 will be the first Pegasus engine.  However it does have a promise library bundled: miniRT/pacts.  Promises are used like in every other promise library in existence, however you also have Pacts to simplify making them:

Code: (javascript) [Select]

var pacts = require('miniRT/pacts');
var myPact = new pacts.Pact();  // make a pact, hopefully not with the devil!
// ...
var promise = myPact.promise();
// ...do a bunch of stuff asynchronously...
myPact.resolve(promise, 812);  // resolve the promise


And if you're particularly evil, you can also do:
Code: (javascript) [Select]

// reject all the outstanding promises
myPact.welsh(new Error("a pig ate everything"));
miniSphere 4.8.2 - Cell compiler - SSj debugger
Forum Thread | GitHub Repo

  • Rahkiin
  • [*]
  • Verified
Re: Future of Sphere: Using Async and Promises
Reply #2
I assume one could also just:

Code: (javascript) [Select]

let Promise = require("miniRT/pacts");
// Do normal promise stuff?

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Future of Sphere: Using Async and Promises
Reply #3
Well it would be more of:
Code: (javascript) [Select]

let Promise = require('miniRT/pacts').Promise;


But otherwise, yes.  The Pacts are just there to simplify JS code which needs to create promises since using new Promise effectively is difficult.
miniSphere 4.8.2 - Cell compiler - SSj debugger
Forum Thread | GitHub Repo

  • Rahkiin
  • [*]
  • Verified
Re: Future of Sphere: Using Async and Promises
Reply #4
Personally I think it works just fine but okay.

Are you against swapping it around?

pact = require().pact;
promise = require();

(hint: IRC #sphere @ irc.esper.net)