packages/ddp-client/common/MethodInvoker.js
// A MethodInvoker manages sending a method to the server and calling the user's
// callbacks. On construction, it registers itself in the connection's
// _methodInvokers map; it removes itself once the method is fully finished and
// the callback is invoked. This occurs when it has both received a result,
// and the data written by it is fully visible.
export default class MethodInvoker {
constructor(options) {
// Public (within this file) fields.
this.methodId = options.methodId;
this.sentMessage = false;
this._callback = options.callback;
this._connection = options.connection;
this._message = options.message;
this._onResultReceived = options.onResultReceived || (() => {});
this._wait = options.wait;
this.noRetry = options.noRetry;
this._methodResult = null;
this._dataVisible = false;
// Register with the connection.
this._connection._methodInvokers[this.methodId] = this;
}
// Sends the method message to the server. May be called additional times if
// we lose the connection and reconnect before receiving a result.
sendMessage() {
// This function is called before sending a method (including resending on
// reconnect). We should only (re)send methods where we don't already have a
// result!
if (this.gotResult())
throw new Error('sendingMethod is called on method with result');
// If we're re-sending it, it doesn't matter if data was written the first
// time.
this._dataVisible = false;
this.sentMessage = true;
// If this is a wait method, make all data messages be buffered until it is
// done.
if (this._wait)
this._connection._methodsBlockingQuiescence[this.methodId] = true;
// Actually send the message.
this._connection._send(this._message);
}
// Invoke the callback, if we have both a result and know that all data has
// been written to the local cache.
_maybeInvokeCallback() {
if (this._methodResult && this._dataVisible) {
// Call the callback. (This won't throw: the callback was wrapped with
// bindEnvironment.)
this._callback(this._methodResult[0], this._methodResult[1]);
// Forget about this method.
delete this._connection._methodInvokers[this.methodId];
// Let the connection know that this method is finished, so it can try to
// move on to the next block of methods.
this._connection._outstandingMethodFinished();
}
}
// Call with the result of the method from the server. Only may be called
// once; once it is called, you should not call sendMessage again.
// If the user provided an onResultReceived callback, call it immediately.
// Then invoke the main callback if data is also visible.
receiveResult(err, result) {
if (this.gotResult())
throw new Error('Methods should only receive results once');
this._methodResult = [err, result];
this._onResultReceived(err, result);
this._maybeInvokeCallback();
}
// Call this when all data written by the method is visible. This means that
// the method has returns its "data is done" message *AND* all server
// documents that are buffered at that time have been written to the local
// cache. Invokes the main callback if the result has been received.
dataVisible() {
this._dataVisible = true;
this._maybeInvokeCallback();
}
// True if receiveResult has been called.
gotResult() {
return !!this._methodResult;
}
}