-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Conversation
@@ -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); |
There was a problem hiding this comment.
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_
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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_
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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_.
There was a problem hiding this comment.
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', { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…named it to sendMessageCancelUnsent
@dvoytenko PTAL |
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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; ...)?
There was a problem hiding this comment.
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 :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for
.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this 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 |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
LGTM on my side, pending the comments from others. |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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]; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect.
There was a problem hiding this comment.
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?
Thanks! merging... |
…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
…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
as part of #6159