Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes viewer.sendMessageUnreliable reliably return promise of response. #6191

Merged
merged 8 commits into from
Nov 17, 2016

Conversation

lannka
Copy link
Contributor

@lannka lannka commented Nov 15, 2016

as part of #6159

@@ -1005,7 +1005,7 @@ export class Viewer {
return Promise.reject(getChannelError());
}
return this.messagingReadyPromise_.then(() => {
return this.sendMessageUnreliable_(eventType, data, awaitResponse);
return this.messageDeliverer_(eventType, data, awaitResponse);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why original code calls sendMessageUnreliable_.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, the message is NOT guaranteed to be delivered. It doesn't wait for the messaging to be established and may or may not be queued.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in the messagingReadyPromise, so it will never be queued. it's exactly the same to call either sendMessageUnreliable_ or messageDeliverer_ here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It reduces duplication, so that only #sendMessageUnreliable knows how to really send a message. This should continue to call the unreliable method (since we've assured it's now "reliable" by waiting for messaging to be established).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jridgewell don't quite understand what duplication we reduced here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imagine we add a 4th param to messageDeliverer function. Now we have to add that to 3 spots instead of just #sendMessageUnreliable.

Copy link
Contributor Author

@lannka lannka Nov 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got you. but logically it doesn't make too much sense because the messaging is there, and we know we can send it "reliablely", why still call "sendUnreliable". Anyway, I don't want to be off the topic here too much since we're actually thinking of comletely removing sendMessageUnreliable_

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is unimportant (this could be called #postMessage and #reliablyPostMessage). The benefit is the de-duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. It doesn't make sense to call

function sendMessage(message) {
  if (messagingReady) {
    sendMessageCancelUnsent(message);
  }
}

@@ -1078,7 +1077,7 @@ export class Viewer {
}
this.messagingMaybePromise_.then(() => {
if (this.messageDeliverer_) {
this.sendMessageUnreliable_(eventType, data, false);
this.messageDeliverer_(eventType, data, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why original code calls sendMessageUnreliable_.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have just checked for #messagingMaybePromise, then called #sendMessage.

@@ -742,7 +742,7 @@ export class Viewer {
* Triggers "documentLoaded" event for the viewer.
*/
postDocumentReady() {
this.sendMessageUnreliable_('documentLoaded', {
this.sendMessageUnreliable('documentLoaded', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to reconsider these calls in an alternative way. We already have a very clear idea whether or not (a) doc is embedded; and (b) where the messaging is expected. With this in mind, I'd rather change this code to either wait for messaging ready and reliably send this message or not even try if messaging is not coming. We currently have, I believe, maybeSendMessage_ that does something like that in Viewer.

Some code in "sendMessageUnreliable" has a useful feature to de-dupe the messages. But it's too coarse and doesn't work very well for all cases. I'd rather move deduplication, if needed, to the actual class that issues messages.

WDYT? So, the bottom line, the sendMessageUnreliable_ will be completely gone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you that the purpose of sendMessageUnreliable is a bit unclear. If you think we can live without the the de-dup logic, then this can be completely gone.

Also want to blame the method for its returned promise, which is indeterministic, caller should not actually rely on it.

I'll change all current usage of sendMessageUnreliable just to sendMessage if it sounds good to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion, de-dup logic is actually hard to implement outside this class.

I made change to sendMessageUnreliable so that it will have a consistent return value. And I renamed it to sendMessageCancelUnsent. Let me know if this makes sense.

@lannka lannka changed the title Makes viewer.sendMessageUnreliable public. Makes viewer.sendMessageUnreliable return promise of response. Nov 15, 2016
@lannka lannka changed the title Makes viewer.sendMessageUnreliable return promise of response. Makes viewer.sendMessageUnreliable reliably return promise of response. Nov 16, 2016
@lannka
Copy link
Contributor Author

lannka commented Nov 16, 2016

@dvoytenko PTAL

@lannka
Copy link
Contributor Author

lannka commented Nov 16, 2016

TODO added. @dvoytenko PTAL

@@ -834,6 +841,7 @@ export class Viewer {
* Get the fragment from the url or the viewer.
* Strip leading '#' in the fragment
* @return {!Promise<string>}
* TODO: move this to amp-share-tracking
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method might need to stay inside viewer-impl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only used by amp-share-tracking. Not sure if anyone else is interested. If later it's really sharable code, we should probably extract it to somewhere else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we decided to move this to history service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not a bad idea

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, should be in history-impl

@@ -863,6 +871,7 @@ export class Viewer {
* The fragment variable should contain leading '#'
* @param {string} fragment
* @return {!Promise}
* TODO: move this to amp-share-tracking, and use sendMessage()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should also stay

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise would go to history?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

history-impl sounds good.

*/
export function findIndex(array, predicate) {
let i = 0;
while (i < array.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect predicate to mutate array? Why not use for(let i = 0; ...)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

predicate should not mutate array.
for and while are the same here. unless you insist :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I surrender.

@@ -834,6 +841,7 @@ export class Viewer {
* Get the fragment from the url or the viewer.
* Strip leading '#' in the fragment
* @return {!Promise<string>}
* TODO: move this to amp-share-tracking
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we decided to move this to history service?

@@ -844,7 +853,7 @@ export class Viewer {
if (!this.hasCapability('fragment')) {
return Promise.resolve('');
}
return this.sendMessageUnreliable_('fragment', undefined, true).then(
return this.sendMessageCancelUnsent('fragment', undefined, true).then(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be switched to getMessage right away. "cancel unsent" really wrong here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What can go wrong? It's still a tiny optimization, no?
Considering multiple getFragment() were queued up, and when messaging channel established, we can just get it once from Viewer and respond to all interested party.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be only one getFragment call.
Without the modified queued up change in this PR, the previous sendMessageUnreliable_ is wrong, since it will get a resolved promise without getting the fragment if the message is sent before message channel is established. But now I think we should use sendMessageCancelUnsent because it now only resolves when the message channel is established and get a response from the viewer, which is exactly what we want.

@@ -863,6 +871,7 @@ export class Viewer {
* The fragment variable should contain leading '#'
* @param {string} fragment
* @return {!Promise}
* TODO: move this to amp-share-tracking, and use sendMessage()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise would go to history?

*/
tick(message) {
this.sendMessageUnreliable_('tick', message, false);
this.sendMessageCancelUnsent('tick', message, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is tricky :( In case of ticks, the first tick is the most important. How can we approach this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, so the TODO is to make it sendMessage(). i think we should not cancel any unsent ones.

}

/**
* Triggers "prerenderComplete" event for the viewer.
* @param {!Object} message
*/
prerenderComplete(message) {
this.sendMessageUnreliable_('prerenderComplete', message, false);
this.sendMessageCancelUnsent('prerenderComplete', message, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO to be moved to either Resources or Performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

performance

if (found != -1) {
message = this.messageQueue_.splice(found, 1)[0];
message.data = data;
message.awaitResponse = awaitResponse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be message.awaitResponse |= awaitResponse?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

// TODO(dvoytenko): This is somewhat questionable. What do we return
// when no one is listening?
return Promise.resolve();
let responseResolver;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always need to create it? Or only the first time we run into awaitResponse = true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logically, yes, no need. but for code simplicity, i think it's harmless?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

const responsePromise = new Promise(r => {
responseResolver = r;
});
message = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not seeing the typedef for the messageQueue_ updated anywhere...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

@lannka lannka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments addressed

@@ -834,6 +841,7 @@ export class Viewer {
* Get the fragment from the url or the viewer.
* Strip leading '#' in the fragment
* @return {!Promise<string>}
* TODO: move this to amp-share-tracking
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not a bad idea

@@ -844,7 +853,7 @@ export class Viewer {
if (!this.hasCapability('fragment')) {
return Promise.resolve('');
}
return this.sendMessageUnreliable_('fragment', undefined, true).then(
return this.sendMessageCancelUnsent('fragment', undefined, true).then(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What can go wrong? It's still a tiny optimization, no?
Considering multiple getFragment() were queued up, and when messaging channel established, we can just get it once from Viewer and respond to all interested party.

@@ -863,6 +871,7 @@ export class Viewer {
* The fragment variable should contain leading '#'
* @param {string} fragment
* @return {!Promise}
* TODO: move this to amp-share-tracking, and use sendMessage()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

history-impl sounds good.

*/
tick(message) {
this.sendMessageUnreliable_('tick', message, false);
this.sendMessageCancelUnsent('tick', message, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, so the TODO is to make it sendMessage(). i think we should not cancel any unsent ones.

if (found != -1) {
message = this.messageQueue_.splice(found, 1)[0];
message.data = data;
message.awaitResponse = awaitResponse;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

// TODO(dvoytenko): This is somewhat questionable. What do we return
// when no one is listening?
return Promise.resolve();
let responseResolver;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logically, yes, no need. but for code simplicity, i think it's harmless?

*/
export function findIndex(array, predicate) {
let i = 0;
while (i < array.length) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

predicate should not mutate array.
for and while are the same here. unless you insist :-)

}

/**
* Triggers "prerenderComplete" event for the viewer.
* @param {!Object} message
*/
prerenderComplete(message) {
this.sendMessageUnreliable_('prerenderComplete', message, false);
this.sendMessageCancelUnsent('prerenderComplete', message, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

performance

message.eventType, message.data, message.awaitResponse);

if (message.awaitResponse) {
responsePromise.then(response => message.responseResolver(response));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can optimize this by resolving with the promise: message.responseResolver(responsePromise)

@@ -1005,7 +1005,7 @@ export class Viewer {
return Promise.reject(getChannelError());
}
return this.messagingReadyPromise_.then(() => {
return this.sendMessageUnreliable_(eventType, data, awaitResponse);
return this.messageDeliverer_(eventType, data, awaitResponse);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is unimportant (this could be called #postMessage and #reliablyPostMessage). The benefit is the de-duplication.

*/
sendMessageUnreliable_(eventType, data, awaitResponse) {
sendMessageCancelUnsent(eventType, data, awaitResponse) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why we care about awaitResponse at all. We always create the promise, why not just return it and let the caller decide if it cares (by calling #then).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's just how the original messageDeliverer function is defined, which can return undefined. It doesn't really matter.

I see one little advantage of returning undefined is that it will throw immediately if you made mistake of awaitResponse=false when it should be true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you want that, though? Why declare my intention twice, with both a boolean and my interacting with the return value (the promise is always generated).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine. but this is how the external messageDeliverer is defined. it's not a bad idea to keep it the same. let's just keep it as is.

*/
export function findIndex(array, predicate) {
let i = 0;
while (i < array.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for.

}
i++;
}
return i < array.length ? i : -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can skip this awkwardness by returning in the loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@dvoytenko
Copy link
Contributor

LGTM on my side, pending the comments from others.

Copy link
Contributor Author

@lannka lannka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All comments addressed.

@@ -1005,7 +1005,7 @@ export class Viewer {
return Promise.reject(getChannelError());
}
return this.messagingReadyPromise_.then(() => {
return this.sendMessageUnreliable_(eventType, data, awaitResponse);
return this.messageDeliverer_(eventType, data, awaitResponse);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. It doesn't make sense to call

function sendMessage(message) {
  if (messagingReady) {
    sendMessageCancelUnsent(message);
  }
}

*/
sendMessageUnreliable_(eventType, data, awaitResponse) {
sendMessageCancelUnsent(eventType, data, awaitResponse) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's just how the original messageDeliverer function is defined, which can return undefined. It doesn't really matter.

I see one little advantage of returning undefined is that it will throw immediately if you made mistake of awaitResponse=false when it should be true.

const responsePromise = new Promise(r => {
responseResolver = r;
});
message = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*/
export function findIndex(array, predicate) {
let i = 0;
while (i < array.length) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I surrender.

}
i++;
}
return i < array.length ? i : -1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


let message;
if (found != -1) {
message = this.messageQueue_.splice(found, 1)[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why splice it out? We can just move the push into the else condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it. we want to cancel the found message

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How have we canceled it? We're just pushing the exact same object (with modifications) back on to the queue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, because we want the new message to be at the tail. Order matters here, think about message like flushTick.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hang on. We are not cancelling the older message are we? We are resolving them all, right?

@lannka
Copy link
Contributor Author

lannka commented Nov 17, 2016

Thanks! merging...

@lannka lannka merged commit 68802ad into ampproject:master Nov 17, 2016
@lannka lannka deleted the viewer_send_message_public branch November 17, 2016 23:53
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
…e. (ampproject#6191)

* Makes viewer.sendMessageUnreliable public.

* Make sendMessageUnreliable to return a real response promise. Also renamed it to sendMessageCancelUnsent

* Make sure new message is added to the tail.

* Add tests.

* nit

* Add TODOs.

* Address comments.

* Address comments
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
…e. (ampproject#6191)

* Makes viewer.sendMessageUnreliable public.

* Make sendMessageUnreliable to return a real response promise. Also renamed it to sendMessageCancelUnsent

* Make sure new message is added to the tail.

* Add tests.

* nit

* Add TODOs.

* Address comments.

* Address comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants