Translation Service
Translation Service

Translation Service

Learning Exercise


The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

The methods promise.then(), promise.catch(), and promise.finally() are used to associate further action with a promise that becomes settled.

For example:

const myPromise = new Promise(function (resolve, reject) {
  let sampleData = [2, 4, 6, 8];
  let randomNumber = Math.ceil(Math.random() * 5);
  if (sampleData[randomNumber]) {
  } else {
    reject('An error occurred!');

  .then(function (e) {
  .catch(function (error) {
    throw new Error(error);
  .finally(function () {
    console.log('Promise completed');


These methods are available on Promise.prototype


The .then() method takes up to two arguments; the first argument is a callback function for the resolved case of the promise, and the second argument is a callback function for the rejected case. Each .then() returns a newly generated promise object, which can optionally be used for chaining.1

const promise1 = new Promise(function (resolve, reject) {

promise1.then(function (value) {
  // expected output: "Success!"


A .catch() is just a .then() without a slot for a callback function for the case when the promise is resolved. It is used to handle rejected promises.2

const promise1 = new Promise((resolve, reject) => {
  throw 'An error occurred';

promise1.catch(function (error) {
// expected output: An error occurred


When the promise is settled, i.e either fulfilled or rejected, the specified callback function is executed. This provides a way for code to be run whether the promise was fulfilled successfully or rejected once the Promise has been dealt with.3

function findDataById(id) {
  return new Promise(function (resolve, reject) {
    let sampleData = [1, 2, 3, 4, 5];
    if (sampleData[id]) {
    } else {
      reject(new Error('Invalid id'));

  .then(function (response) {
  .catch(function (err) {
  .finally(function () {
    console.log('Promise completed');

  1. then, MDN.

  2. catch, MDN.

  3. finally, MDN.


In this exercise, you'll be providing a TranslationService that provides basic translation services to free members, and advanced translation to premium members with quality assurances.


You have found an outer space translation API that fulfills any translation request in a reasonable amount of time. You want to capitalize on this. The space translators are extremely fickle and hate redundancy, so they also provide API storage satellites where you can fetch past translations without bothering them.

Fetching a translation

api.fetch(text) fetches a translation of text from the API storage and returns a promise that provides two values:

  • translation: the actual translation
  • quality: the quality expressed as a number

If a translation is not found in the API storage, the API throws a NotAvailable error. Translations can be added using the api.request method. If 'text' is not translatable, the API throws an Untranslatable error.

// => Promise({ resolved: 'I understand' })

Requesting a translation

Some translations are sure to exist, but haven't been added to the API storage yet. That's the difference between NotAvailable ( not in storage, but can be requested ) and Untranslatable ( cannot be translated ).

api.request(text, callback) requests that a translation of text be performed and added into the API storage. On completion the callback function is called.

  • On success callback is passed undefined: this indicates the translation was successful and is accessible using the api.fetch method.
  • On failure callback is passed an error: this indicates something went wrong. The outspace API is unstable, which means that the API fails often. If that happens, it is okay to api.request again.
api.request('majQa’', callback);
// => undefined
// later: the passed callback is called with undefined
//        because it was successful.

⚠ Warning! ⚠


The API works its magic by teleporting in the various translators when a request comes in. This is a very costly action, so it shouldn't be called when a translation is available. Unfortunately, not everyone reads the manual, so there is a system in place to kick-out bad actors.

If an api.request is called for text is available, the API throws an AbusiveClientError for this call, and every call after that. Ensure that you never request a translation if something has already been translated.

1. Fetch a translation, ignoring the quality

The free service only provides translations that are currently in the API storage.

Implement a method free(text) that provides free members with translation that already exist in the API storage. Ignore the quality and forward any errors thrown by the API.

  • Returns the translation if it can be retrieved, regardless of its quality
  • Forwards any error from the translation API
  • Uses the api.fetch method (api.fetch returns a promise)'jIyaj');
// => Promise<...> resolves "I understand.""jIyajbe'");
// => Promise<...> rejects Error("Not yet translated")

2. Fetch a batch of translations, all-or-nothing

Implement a method batch([text, text, ...]) for free members that translates an array of text using the free service, returning all the translations, or a single error.

  • Resolves with all the translations (in the same order), if they are all available
  • Rejects with the first error that is encountered
  • Rejects with a BatchIsEmpty error if no texts are given
service.batch(['jIyaj', "majQa'"]);
// => Promise<...> resolves ["I understand.", "Well done!"]

service.batch(['jIyaj', "jIyajbe'"]);
// => Promise<...> rejects new Error("Not yet translated")

// => Promise<...> rejects BatchIsEmpty()

3. Request a translation, retrying at most 2 times

Implement a premium user method request(text), that requests a translation be added to the API storage. The request should automatically retry if a failure occurs. It should perform no more than 3 calls for the same request (don't upset the space translators!!!).

  • If api.request does not return an error, resolve with undefined
  • If api.request returns an error, retry at most two times
  • If you run out of retries, reject with the last error received
// => Promise<...> resolves (with nothing), can now be retrieved using the fetch API

4. Fetch a translation, inspect the quality, or request it

Implement a premium user method premium(text, quality) to fetch a translation. If a translation is NotAvailable, request the translation and fetch it after its been added to the API storage. The method should only return the translation if it meets a certain quality threshold.

  • If api.fetch resolves, check the quality before resolving
  • If api.fetch rejects, request the translation instead
  • If api.request rejects, forward the error
service.premium("jIyajbe'", 100);
// => Promise<...> resolves "I don't understand."

service.premium("'arlogh Qoylu'pu'?", 100);
// => Promise<...> rejects QualityThresholdNotMet()

service.premium("'arlogh Qoylu'pu'?", 40);
// => Promise<...> resolves "What time is it?"



The correct translation of 'arlogh Qoylu'pu'? is How many times has it been heard?.

Edit via GitHub The link opens in a new window or tab
JavaScript Exercism

Ready to start Translation Service?

Sign up to Exercism to learn and master JavaScript with 33 concepts, 147 exercises, and real human mentoring, all for free.