Friday, March 24, 2017

My First Google Chrome Extension! And Asynchronous Functions in Javascript

A while ago I published my first Google chrome extension, which you can find here. Basically the extension is an assignment tracker that turns your new tab page into a notepad. It also allows you to highlight text in the browser and right-click to add it to the assignment tracker clipboard.

Although the new tab notepad idea has already been implemented before, I wanted a notepad that would allow me to add text I see in my browser to the notepad without copying and pasting and without opening the new tabs window. My extension Assignment Tracker solves this problem.

The Assignment Tracker was easy to implement because I was able to study code of other google extensions I found on Github, but the add assignment part of my extension was performing unreliably. Fortunately, I discovered the problem later during Incube*.

At Incube, my friends and I are working on an extension that creates a dislike button for Facebook, and I discovered Assignment Tracker's problem while trying to fix something similar. It turns out that there exists something called an Asynchronous method invocation.

In my extension, I was using the chrome.storage API to store text users added to their assignment tracker clipboard, but chrome.storage is designed to be asynchronous; other processes can happen while the chrome.storage function completes. This messed up the chronology of how text was stored and updated on the new tabs page. As a result, selected text didn't always show up on the new tabs clipboard.

I used the get and set functions of chrome.storage to retrieve and edit stored text. These functions take two parameters: the first is the variable we want to retrieve or modify, and the second is the function that will be called after the get or set function has completed.

By nesting the set function inside the second function parameter of the get, I was reliably able to modify the clipboard text to include the new highlighted text information. I have included my solution in the following excerpt:

function add(event) {
  if (event.selectionText) {
    alert("Added to Assignment Tracker: \n\n" + event.selectionText);

    chrome.storage.sync.get("data", function(object) {
      text = object.data;

      if (text === "undefined") newtext = event.selectionText;
      else newtext = event.selectionText + "\n\n" + text;

      chrome.storage.sync.set({ "data" : newtext }, function() {
      if (chrome.runtime.error) console.log("Runtime error.");
      });
    });
  };
};

I am still trying to learn JavaScript so this stack overflow answer may be able to explain the situation more clearly. Because of this annoying experience, I also learned about the bad programming practice of callback hell.


A quote really stood out to me from the callback hell article that I linked above:
"Write small modules that each do one thing, and assemble them into other modules that do a bigger thing. You can't get into callback hell if you don't go there." 
- Isaac Schlueter (creator of npm)

In the future, I will be more careful to avoid writing modules that are too big and hard to debug.

Cheers!

* Incube is an organization where a bunch of people work on projects and eat Indian food together.

0 comments:

Post a Comment

Leave a comment!