From 951d079fe20a7219083c9dea96fc2bd15ef5a81d Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Thu, 30 Jan 2025 10:13:15 -0800 Subject: [PATCH] [JS] fix: Fixed stuck typing indicator issue (#2286) ## Linked issues closes: #2285 ## Details The stuck typing indicator appears to occur when we send a `message` activity while we're in the middle of sending a `typing` activity. The fix is to delay the outgoing `message` activity until after we finish sending the `typing` activity. #### Change details > Created a separate promise variable so that we can have two threads waiting for the same operation to complete. This lets us block the outgoing message activity until after the typing activity finishes delivering. ## Attestation Checklist - [x] My code follows the style guidelines of this project - I have checked for/fixed spelling, linting, and other errors - I have commented my code for clarity - I have made corresponding changes to the documentation (updating the doc strings in the code is sufficient) - My changes generate no new warnings - I have added tests that validates my changes, and provides sufficient test coverage. I have tested with: - Local testing - E2E testing in Teams - New and existing unit tests pass locally with my changes --- js/packages/teams-ai/src/Application.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/js/packages/teams-ai/src/Application.ts b/js/packages/teams-ai/src/Application.ts index ca9f7ad13..522f9bc98 100644 --- a/js/packages/teams-ai/src/Application.ts +++ b/js/packages/teams-ai/src/Application.ts @@ -1021,7 +1021,7 @@ export class Application { public startTypingTimer(context: TurnContext): void { if (context.activity.type == ActivityTypes.Message && !this._typingTimer) { // Listen for outgoing activities - context.onSendActivities((context, activities, next) => { + context.onSendActivities(async (context, activities, next) => { // Listen for any messages to be sent from the bot if (timerRunning) { for (let i = 0; i < activities.length; i++) { @@ -1029,6 +1029,10 @@ export class Application { // Stop the timer this.stopTypingTimer(); timerRunning = false; + + // Wait for the last "typing" activity to finish sending + // - This prevents a race condition that results in the typing indicator being stuck on. + await lastSend; break; } } @@ -1038,10 +1042,12 @@ export class Application { }); let timerRunning = true; + let lastSend: Promise = Promise.resolve(); const onTimeout = async () => { try { // Send typing activity - await context.sendActivity({ type: ActivityTypes.Typing }); + lastSend = context.sendActivity({ type: ActivityTypes.Typing }); + await lastSend; } catch (err) { // Seeing a random proxy violation error from the context object. This is because // we're in the middle of sending an activity on a background thread when the turn ends. @@ -1049,6 +1055,7 @@ export class Application { // eat the error but lets make sure our states cleaned up a bit. this._typingTimer = undefined; timerRunning = false; + lastSend = Promise.resolve(); } // Restart timer