1

Ive brought this up with other developers, and they say while there is no obvious way forward, it seems that they all have an idea of how to go about it.

Is there any way I can implement signals and slots javascript and or typescript? I want to get as close to this functionality as possible: https://doc.qt.io/qt-5/signalsandslots.html

And a code example to demonstrate what I mean:

#include <QObject>
class Label : public QObject
{
    Q_OBJECT

public:
    Label( QObject *parent=nullptr );
    QString label()
    {
        return removePlaceholders(m_Label);
    } 
    void setLabel(QString label)
    {
        m_Label = label;
        emit labelChanged(label);
    }

signals:
    void labelChanged(QString label);

private:
    QString m_Label;
};

which can be utilized like so:

Label label;

QEventLoop waitForLabel;
connect(&label, &Label::labelChanged, &waitForLabel, &QEventLoop::quit);
waitForLabel.exec; // Now the thread can not move past this point, 
// will have to wait for the signal Label::labelChanged to be emitted
// from the Label::setLabel function in the label object.
Anon
  • 3,565
  • 3
  • 27
  • 45
  • Which part(s) of it do you want to implement? At least part of the value of the system is that it allows strongly typed objects to be loosely coupled manner, which is much less of a problem in a weakly typed language like JavaScript. – Philip Kendall Sep 28 '19 at 06:07
  • Well, you certainly can't have threads in JavaScript so that's out. (And are you sure about that? The doc you linked to explicitly says the slot call is synchronous) – Philip Kendall Sep 28 '19 at 06:33
  • @PhilipKendall Yes you are right, it is synchronous. Sorry that was some confusion on my part. – Anon Sep 28 '19 at 06:55
  • 1
    This is a good conceptional question for this site, perfectly on-topic, though some of those hostile downvoters around here don't get it. However, my actual "answer" is very short, so I leave it as a comment: what you are looking for are [custom events](https://gomakethings.com/custom-events-with-vanilla-javascript/). – Doc Brown Sep 28 '19 at 07:37

2 Answers2

4

Signals and slots are an implementation of an event system or of the observer pattern. Those are not tied to C++ but can be implemented in any language. Unfortunately, naive implementations of the observer pattern tend to lead to memory leaks.

The gist is that signals are not actually magic but:

  • a list of callbacks (“slots“) that shall be called when an event occurs.
  • a method to trigger the event, which means calling each callback.

Qt introduced an explicit language-level signal/slots mechanism because C++ was originally really bad at stuff like callbacks.

Javascript does not have this difficulty because you can just use functions. Instead of connect(source, &A::signal, handler, &Some::slot) you might just write source.onSignal.push(() => handler.slot()).

It is also debatable whether registering per-object event handlers is a sensible approach to structure event-driven programs. An alternative is that a central broker tracks dependencies between event-driven code (sometimes called a Reactor). There are also approaches such as functional-reactive programming that strive to be more declarative. Often, the motivation for event-based programming is an asynchronous program, but here JS offers a better approach with its async/await syntax. JavaScript has an unusually rich ecosystem of different event system implementations.

amon
  • 132,749
  • 27
  • 279
  • 375
  • Using your method, what would it look like then to define a signal in my class `signals: void labelChanged(QString label);`, emit it when calling `void setLabel(QString label)`, and being able to connect and disconnect that signal to other objects or functions? Would you advise creating some syntactical sugar for this, or would this be too complicated? – Anon Sep 29 '19 at 02:07
  • 1
    @Akiva No need for syntactic sugar. A signal is a list of even handlers that can be registered by other objects. The signal would be triggered by calling some internal method, e.g. `function signalLabelChanged() { this.onLabelChanged.forEach(handler => handler()); }`. The `setLabel()` function would then call the `signalLabelChanged()` method when a change is detected. – amon Sep 29 '19 at 16:15
1

For those interested, this is what I ended up implementing as my basic class structure

    /* Boiler Plate */
    // Getters
    public table() : string { return this.m_Table; }

    // Setters
    public setTable(table: string) { this.m_Table = table; this.tableChanged(table); }

    // Signals
    private tableChanged(table: string): void {this.onTableChanged.forEach(f=>f(table));}

    // Slots
    public onTableChanged: Array<(table: string) => void> = new Array;

    // Member variables
    private m_Table: string;
let psql = new QPostgreSQL( "db_qpoll", "/*/*/*/*/*][/" );
psql.onTableChanged.push((s) => console.log(s); ); // You can toss functions in here. 
psql.setTable( '"Question"."Questions"' );

or in picture form : enter image description here

The only thing I wish I had now, are text preprocessor macros to simplify everything here.

Anon
  • 3,565
  • 3
  • 27
  • 45