Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions packages/perspective/src/js/api/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class Client {
}

/**
* Process an asynchronous message.
* Send a message to the server.
*/
post(msg, resolve, reject, keep_alive = false) {
++this._worker.msg_id;
Expand All @@ -55,7 +55,13 @@ export class Client {
if (this._worker.initialized.value) {
this.send(msg);
} else {
this._worker.messages.push(() => this.send(msg));
this._worker.messages.push(() => {
this.send(msg);

if ((msg.cmd === "table" || msg.cmd === "view") && !this._features?.wait_for_response && resolve) {
resolve();
}
});
}
}

Expand Down Expand Up @@ -87,10 +93,8 @@ export class Client {
}

/**
* Handle a command from Perspective. If the Client is not initialized,
* initialize it and dispatch the `perspective-ready` event.
*
* Otherwise, reject or resolve the incoming command.
* Receive a message from the server, and resolve/reject the promise that
* is awaiting the content of the message.
*/
_handle(e) {
if (!this._worker.initialized.value) {
Expand All @@ -107,6 +111,16 @@ export class Client {
this._worker.initialized.value = true;
this._worker.messages = [];

// If the `data` attribute of the init message is set, then
// set the `features` dictionary with the flags from the server.
if (e.data?.data) {
this._features = {};

for (const feature of e.data.data) {
this._features[feature] = true;
}
}

if (msgs) {
for (const m in msgs) {
if (msgs.hasOwnProperty(m)) {
Expand All @@ -117,13 +131,15 @@ export class Client {
}

if (e.data.id) {
var handler = this._worker.handlers[e.data.id];
const handler = this._worker.handlers[e.data.id];

if (handler) {
if (e.data.error) {
handler.reject(e.data.error);
} else {
handler.resolve(e.data.data);
}

if (!handler.keep_alive) {
delete this._worker.handlers[e.data.id];
}
Expand Down
11 changes: 11 additions & 0 deletions packages/perspective/src/js/api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@ export class Server {
}

/**
* Return an initialization message to the client for confirmation.
* `Server` must be extended and the `post` method implemented before the
* server can successfully be initialized.
*/
init(msg) {
if (msg.config) {
override_config(msg.config);
}

// The client will wait for a response message on table() and
// view(). If this flag is not set, the table() and view()
// constructors will resolve automatically and errors from the
// server will not be caught in those constructors. This allows
// for backwards compatibility between newer frontends (those
// with async table/view constructors) and older servers (which
// do not send the response message to the client).
msg.data = ["wait_for_response"];

this.post(msg);
}

Expand Down
4 changes: 4 additions & 0 deletions packages/perspective/src/js/api/table_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export function table(worker, data, options) {
reject
);
}

if (this._worker._initialized === true && !this._worker._features?.wait_for_response) {
resolve(this);
}
});
}

Expand Down
4 changes: 4 additions & 0 deletions packages/perspective/src/js/api/view_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export function view(worker, table_name, config) {
},
reject
);

if (this._worker._initialized === true && !this._worker._features?.wait_for_response) {
resolve(this);
}
});
}

Expand Down
10 changes: 8 additions & 2 deletions python/perspective/perspective/manager/manager_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,14 @@ def __process(self, msg, post_callback, client_id=None):

try:
if cmd == "init":
# return empty response
message = self._make_message(msg["id"], None)
# The client should wait for the return message on table()
# and view() to confirm successful creation.
flags = ["wait_for_response"]

# Return a message to the client to confirm initialization with
# a list of feature flags that the client will use to enable
# or disable behavior depending on its version.
message = self._make_message(msg["id"], flags)
post_callback(self._message_to_json(msg["id"], message))
elif cmd == "table":
try:
Expand Down