Showing posts with label ES6. Show all posts
Showing posts with label ES6. Show all posts

Monday 1 March 2021

Implementing ToDictionary in Typescript

In this article I will present some code I just did in my SimpleTsLinq library, which you can easily install using Npm. The library is here on Npmjs.com : The ToDictionary method looks like this:

  
  if (!Array.prototype.ToDictionary) {
  Array.prototype.ToDictionary = function <T>(keySelector: (arg: T) => any): any {
    let hash = {};
    this.map(item => {
      let key = keySelector(item);
      if (!(key in hash)) {
        hash[key] = item;
      }
      else {
        if (!(Array.isArray(hash[key]))) {
          hash[key] = [hash[key]];
        }
        hash[key].push(item);
      }
    });
    return hash;
  }
}
  

Here is a unit test (spec) for this method :

  
    it('can apply method ToDictionary on an array, allowing specificaton of a key selector for the dictionary object', () => {
    let heroes = [{ name: "Han Solo", age: 47, gender: "M" }, { name: "Leia", age: 29, gender: "F" }, { name: "Luke", age: 24, gender: "M" }, { name: "Lando", age: 47, gender: "M" }];
    let dictionaryOfHeroes = heroes.ToDictionary<Hero>(x => x.gender);

    let expectedDictionary = {
      "F": {
        name: "Leia", age: 29, gender: "F"
      },
      "M": [
        { name: "Han Solo", age: 47, gender: "M" },
        { name: "Luke", age: 24, gender: "M" },
        { name: "Lando", age: 47, gender: "M" }
      ]
    };
    expect(dictionaryOfHeroes).toEqual(expectedDictionary);
  });
  
  

You can also test out this library using Npm RunKit here: We can make a dictionary with different keys, image example:

Saturday 4 January 2020

Implementing GetPropertyNames in Typescript

I am currently working on a Linq-like library for Typescript and wanted to implement something like GetProperties of C# in Typescript / Javascript. The more I work with Typescript and generics, the clearer picture I get of that you usually have to have an instantiated object with intialized properties to get any useful information out at runtime about properties of a class. But it would be nice to retrieve information anyways just from the constructor function object, or an array of objects and be flexible about this. I was following a question thread on Stack Overflow and found a good answer that helped me out: https://stackoverflow.com/questions/40636292/get-properties-of-a-class/59586570#59586570 Here is what I ended up with for now. First off, I define Array prototype method ('extension method' for you C# developers).

export { } //creating a module of below code
declare global {
  interface Array>T< {
    GetProperties>T<(TClass: Function, sortProps: boolean): string[];
} }
The GetProperties method then looks like this, inspired by madreasons answer.

if (!Array.prototype.GetProperties) {
  Array.prototype.GetProperties = function >T<(TClass: any = null, sortProps: boolean = false): string[] {
    if (TClass === null || TClass === undefined) {
      if (this === null || this === undefined || this.length === 0) {
        return []; //not possible to find out more information - return empty array
      }
    }
    // debugger
    if (TClass !== null && TClass !== undefined) {
      if (this !== null && this !== undefined) {
        if (this.length < 0) {
          let knownProps: string[] = Describer.describe(this[0]).Where(x =< x !== null && x !== undefined);
          if (sortProps && knownProps !== null && knownProps !== undefined) {
            knownProps = knownProps.OrderBy(p =< p);
          }
          return knownProps;
        }
        if (TClass !== null && TClass !== undefined) {
          let knownProps: string[] = Describer.describe(TClass).Where(x =< x !== null && x !== undefined);
          if (sortProps && knownProps !== null && knownProps !== undefined) {
            knownProps = knownProps.OrderBy(p =< p);
          }
          return knownProps;
        }
      }
    }
    return []; //give up..
  }
}

The describer method is about the same as madreason's answer on Stack Overflow concerning this. It can handle both class Function and if you get an object instead. It will then use Object.getOwnPropertyNames if no class Function is given (i.e. the class 'type' for C# developers).

class Describer {
  private static FRegEx = new RegExp(/(?:this\.)(.+?(?= ))/g);
  static describe(val: any, parent = false): string[] {
    let isFunction = Object.prototype.toString.call(val) == '[object Function]';
    if (isFunction) {
      let result = [];
      if (parent) {
        var proto = Object.getPrototypeOf(val.prototype);
        if (proto) {
          result = result.concat(this.describe(proto.constructor, parent));
        }
      }
      result = result.concat(val.toString().match(this.FRegEx));
      result = result.Where(r =< r !== null && r !== undefined);
      return result;
    }
    else {
      if (typeof val == "object") {
        let knownProps: string[] = Object.getOwnPropertyNames(val);
        return knownProps;
      }
    }
    return val !== null ? [val.tostring()] : [];
  }
}

Here you see two specs for testing this out with Jasmine.

class Hero {
  name: string;
  gender: string;
  age: number;
  constructor(name: string = "", gender: string = "", age: number = 0) {
    this.name = name;
    this.gender = gender;
    this.age = age;
  }
}

class HeroWithAbility extends Hero {
  ability: string;
  constructor(ability: string = "") {
    super();
    this.ability = ability;
  }
}

describe('Array Extensions tests for TsExtensions Linq esque library', () =< {

  it('can retrieve props for a class items of an array', () =< {
    let heroes: Hero[] = [>Hero<{ name: "Han Solo", age: 44, gender: "M" }, >Hero<{ name: "Leia", age: 29, gender: "F" }, >Hero<{ name: "Luke", age: 24, gender: "M" }, >Hero<{ name: "Lando", age: 47, gender: "M" }];
    let foundProps = heroes.GetProperties(Hero, false);
    //debugger
    let expectedArrayOfProps = ["name", "age", "gender"];
    expect(foundProps).toEqual(expectedArrayOfProps);
    expect(heroes.GetProperties(Hero, true)).toEqual(["age", "gender", "name"]);
  });

  it('can retrieve props for a class only knowing its function', () =< {
    let heroes: Hero[] = [];
    let foundProps = heroes.GetProperties(Hero, false);
    let expectedArrayOfProps = ["this.name", "this.gender", "this.age"];
    expect(foundProps).toEqual(expectedArrayOfProps);
    let foundPropsThroughClassFunction = heroes.GetProperties(Hero, true);
    //debugger
    expect(foundPropsThroughClassFunction.SequenceEqual(["this.age", "this.gender", "this.name"])).toBe(true);
  });

..

And as madreason mentioned, you have to initialize the props to get any information out from just the class Function itself, or else it is stripped away when Typescript code is turned into Javascript code. Typescript 3.7 is very good with Generics, but coming from a C# and Reflection background, some fundamental parts of Typescript and generics still feels somewhat loose and unfinished business. Like my code here, but at least I got out the information I wanted - a list of property names for a given class or instance of objects.

Tuesday 31 December 2019

Aggregate in Typescript

I have implemented Aggregate method in my Linq library written for Typescript.

if (!Array.prototype.Aggregate) {
  Array.prototype.Aggregate = function <T>(accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any {
    //debugger
    if (reducerFunc === undefined || reducerFunc === null) {
      reducerFunc = (accumulator, currentValue) => accumulator + currentValue;
    }
    let result = this.reduce(reducerFunc);
    return result;
  }
}

if (!Array.prototype.AggregateSelect) {
  Array.prototype.AggregateSelect = function <T>(property: (keyof T), accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any {
    //debugger
    if (reducerFunc === undefined || reducerFunc === null) {
      reducerFunc = (accumulator, currentValue) => accumulator + currentValue;
    }
    //debugger
    let result = this.Select(property).map(n => n[property]).reduce(reducerFunc);
    return result;
  }
}

Here are some Jasmine tests for these two methods - note that I also support setting the initial value.
  it('can aggregate items to expected result using Aggregate on array of items of numbers', () => {
    let someNums = [1, 2, 3, 4];
    let result = someNums.Aggregate(0, 0, null);
    expect(result).toBe(10);
  });

  it('can aggregate items and project to expected result using AggregateSelect on array of items of objects', () => {
    let someArray: any[] = [];
    someArray.push(<SomeClass>{ Name: "Foo", Num: 1 });
    someArray.push(<SomeClass>{ Name: "FooBaz", Num: 4 });
    someArray.push(<SomeClass>{ Name: "AllyoBaze", Num: 7 });
    let result = someArray.AggregateSelect<SomeClass>("Num", 0, 0, null);
    expect(result).toBe(12);
  });

  it('can aggregate once more items and project to expected result using AggregateSelect on array of items of objects with accumulator value set initially', () => {
    let someArray: Student[] = [];
    someArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
    someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    someArray.push(<Student>{ StudentID: 3, StudentName: "Bill", Age: 18 });
    someArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
    someArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
    let result = someArray.AggregateSelect<Student>("StudentName", "Student Names: ", 0, (a, b) => a + "," + b);
    expect(result).toBe("John,Moin,Bill,Ram,Ron");
  });

My interface definition is growing everytime for my Linq Library! Here is how it looks now:

export { } //creating a module of below code
declare global {
  type predicate<T> = (arg: T) => boolean;
  type sortingValue<T> = (arg: T) => any;
  interface Array<T> {
    FirstOrDefault<T>(condition: predicate<T>): T;
    LastOrDefault<T>(condition: predicate<T>): T;
    Where<T>(condition: predicate<T>): T[];
    Count<T>(): number;
    CountBy<T>(condition: predicate<T>): number;
    Select<T>(...properties: (keyof T)[]): any[];
    GroupBy<T>(groupFunc: (arg: T) => string): any[];
    EnumerableRange(start: number, count: number): number[];
    Any<T>(condition: predicate<T>): boolean;
    All<T>(condition: predicate<T>): boolean;
    MaxSelect<T>(property: (keyof T)): any;
    MinSelect<T>(property: (keyof T)): any;
    Average<T>(): number;
    AverageSelect<T>(property: (keyof T)): number;
    Max(): any;
    Min(): any;
    Sum(): any;
    Distinct<T>(): T[];
    DistinctBy<T>(property: (keyof T)): any;
    SumSelect<T>(property: (keyof T)): any;
    Intersect<T>(otherArray: T[]): T[];
    IntersectSelect<T>(property: (keyof T), otherArray: T[]): T[];
    MinSelect<T>(property: (keyof T)): any;
    OrderBy<T>(sortMember: sortingValue<T>): T[];
    OrderByDescending<T>(sortMember: sortingValue<T>): T[];
    ThenBy<T>(sortMember: sortingValue<T>): T[];
    OfType<T>(compareObject: T): T[];
    SequenceEqual<T>(compareArray: T): boolean;
    Take<T>(count: number): T[];
    TakeWhile<T>(condition: predicate<T>): T[];
    SkipWhile<T>(condition: predicate<T>): T[];
    Skip<T>(count: number): T[];
    defaultComparerSort<T>(x: T, y: T);
    ElementAt<T>(index: number);
    ElementAtOrDefault<T>(index: number);
    Aggregate<T>(accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any;
    AggregateSelect<T>(property: (keyof T), accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any;
  }
}

DistinctBy operator written in Typescript

I am extended my Linq library for Typescript with many more methods! Here is my implementation of DistinctBy.

if (!Array.prototype.DistinctBy) {
  Array.prototype.DistinctBy = function <T>(property: (keyof T)): T[] {
    if (this === null || this === undefined) {
      return [];
    }
    let filteringArray = this.Select(property).map(n => n[property]);

    let distinctRunOnArray = this.filter((value, index, array) => {
      let valueProperty = value[property];
      return filteringArray.indexOf(valueProperty) === index;
    });
    return distinctRunOnArray;
  }
}

This Jasmine test can test this operator out.
describe('TSLinq Jasmine tests', () => {

  it('can filter out duplicates using DistinctBy on array of items of objects', () => {
    let someArray: Student[] = [];
    someArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
    someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    someArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
    someArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
    let expectedArray: Student[] = [];
    expectedArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
    expectedArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    expectedArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
    expectedArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
    let result = someArray.DistinctBy<Student>("StudentID");
    expect(result).toEqual(expectedArray);
  });


});
The Student class is simple:

class Student {
  StudentID: number;
  StudentName: string;
  Age: number;
}

Friday 27 December 2019

Implementing Linq methods on arrays with Typescript for Angular 8

This article will look into implementing Linq methods on array with Typescript for Angular 8. First off, I have created a repo for this article on Github. Simple Linq Library written with Typescript for Angular 8 This only implements FirstOrDefault and Where Linq operators on arrays. We first need to define our Array prototype methods. Since we use Angular, first we define an empty module using export {} and then declare global { .. } Inside our declare global we define our type predicate and our methods Where and FirstOrDefault. Then we define our two methods if they do not exist yet on Array.prototype. The special syntax above is adaptions for Typescript and Angular. I have tested this with Angular 8. Here is the Typescript code I ended up with:

export { } //creating a module of below code
declare global {
  type predicate<T> = (arg: T) => boolean;
  interface Array<T> {
    FirstOrDefault<T>(condition: predicate<T>): T;
    Where<T>(condition: predicate<T>): T[];
  }
}

if (!Array.prototype.FirstOrDefault) {
  Array.prototype.FirstOrDefault = function <T>(condition: predicate<T>): T {
    let matchingItems: T[] = this.filter((item: T) => {

      if (condition(item)) {
        return item;
      }
    });
    if (matchingItems.length > 0) {
      return matchingItems[0];
    }
    return null;
  }
}

if (!Array.prototype.Where) {
  Array.prototype.Where = function <T>(condition: predicate<T>): T[] {

    let matchingItems: T[] = this.filter((item: T) => {

      if (condition(item)) {
        return true;
      }
    });
    return matchingItems;
  }
}

Let us define some input data - an array to work on and pass into predicates where we can test out FirstOrDefault and Where methods !

import { Movie } from './movie';

export const StarWarsMovies : Array>Movie< =
 [{
      "title" : "Star Wars: Episode I - The Phantom Menace",
      "episode_number" : "1",
      "main_characters" : ["Qui-Gon Jinn", "Obi-Wan Kenobi", "Anakin Skywalker", "Padmé Amidala", "Jar Jar Binks", "Darth Maul"],
      "description" : "The evil Trade Federation, led by Nute Gunray is planning to take over the peaceful world of Naboo. Jedi Knights Qui-Gon Jinn and Obi-Wan Kenobi are sent to confront the leaders. But not everything goes to plan. The two Jedi escape, and along with their new Gungan friend, Jar Jar Binks head to Naboo to warn Queen Amidala, but droids have already started to capture Naboo and the Queen is not safe there. Eventually, they land on Tatooine, where they become friends with a young boy known as Anakin Skywalker. Qui-Gon is curious about the boy, and sees a bright future for him. The group must now find a way of getting to Coruscant and to finally solve this trade dispute, but there is someone else hiding in the shadows. Are the Sith really extinct? Is the Queen really who she says she is? And what's so special about this young boy?",
      "poster" : "star_wars_episode_1_poster.png",
      "hero_image" : "star_wars_episode_1_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode II - Attack of the Clones",
      "episode_number" : "2",
      "main_characters" : ["Obi-Wan Kenobi", "Anakin Skywalker", "Count Dooku", "Padmé Amidala", "Mace Windu", "Yoda", "Jango Fett", "Supreme Chancellor Palpatine"],
      "description" : "Ten years after the 'Phantom Menace' threatened the planet Naboo, Padmé Amidala is now a Senator representing her homeworld. A faction of political separatists, led by Count Dooku, attempts to assassinate her. There are not enough Jedi to defend the Republic against the threat, so Chancellor Palpatine enlists the aid of Jango Fett, who promises that his army of clones will handle the situation. Meanwhile, Obi-Wan Kenobi continues to train the young Jedi Anakin Skywalker, who fears that the Jedi code will forbid his growing romance with Amidala.",
      "poster" : "star_wars_episode_2_poster.png",
      "hero_image" : "star_wars_episode_2_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode III - Revenge of the Sith",
      "episode_number" : "3",
      "main_characters" : ["Obi-Wan Kenobi", "Anakin Skywalker", "Count Dooku", "Padmé Amidala", "Mace Windu", "Yoda", "C-3PO", "Supreme Chancellor Palpatine"],
      "description" : "Three years after the onset of the Clone Wars; the noble Jedi Knights are spread out across the galaxy leading a massive clone army in the war against the Separatists. After Chancellor Palpatine is kidnapped, Jedi Master Obi-Wan Kenobi and his former Padawan, Anakin Skywalker, are dispatched to eliminate the evil General Grievous. Meanwhile, Anakin's friendship with the Chancellor arouses suspicion in the Jedi Order, and dangerous to the Jedi Knight himself. When the sinister Sith Lord, Darth Sidious, unveils a plot to take over the galaxy, the fate of Anakin, the Jedi order, and the entire galaxy is at stake. Upon his return, Anakin Skywalker's wife Padme Amidala is pregnant, but he is having visions of her dying in childbirth. Anakin Skywalker ultimately turns his back on the Jedi, thus completing his journey to the dark side and his transformation into Darth Vader. Obi-Wan Kenobi must face his former apprentice in a ferocious lightsaber duel on the fiery world of Mustafar.",
      "poster" : "star_wars_episode_3_poster.png",
      "hero_image" : "star_wars_episode_3_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode IV - A New Hope",
      "episode_number" : "4",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Ben Kenobi", "Darth Vader", "C-3P0", "R2-D2", "Chewbacca"],
      "description" : "Part IV in George Lucas' epic, Star Wars: A New Hope opens with a Rebel ship being boarded by the tyrannical Darth Vader. The plot then follows the life of a simple farm boy, Luke Skywalker, as he and his newly met allies (Han Solo, Chewbacca, Obi-Wan Kenobi, C-3PO, R2-D2) attempt to rescue a Rebel leader, Princess Leia, from the clutches of the Empire. The conclusion is culminated as the Rebels, including Skywalker and flying ace Wedge Antilles make an attack on the Empire's most powerful and ominous weapon, the Death Star.",
      "poster" : "star_wars_episode_4_poster.png",
      "hero_image" : "star_wars_episode_4_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode V - The Empire Strikes Back",
      "episode_number" : "5",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Darth Vader", "C-3P0", "R2-D2", "Chewbacca", "Lando Calrissian", "Boba Fett"],
      "description" : "Fleeing the evil Galactic Empire, the Rebels abandon their new base in an assault with the Imperial AT-AT walkers on the ice world of Hoth. Princess Leia, Han Solo, Chewbacca and the droid C-3PO escape in the Millennium Falcon, but are later captured by Darth Vader on Bespin. Meanwhile, Luke Skywalker and the droid R2-D2 follows Obi-Wan Kenobi's posthumous command, and receives Jedi training from Master Yoda on the swamp world of Dagobah. Will Skywalker manage to rescue his friends from the Dark Lord?",
      "poster" : "star_wars_episode_5_poster.png",
      "hero_image" : "star_wars_episode_5_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode VI - Return of the Jedi",
      "episode_number" : "6",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Darth Vader", "C-3P0", "Chewbacca", "The Emperor", "Boba Fett"],
      "description" : "Darth Vader and the Empire are building a new, indestructible Death Star. Meanwhile, Han Solo has been imprisoned, and Luke Skywalker has sent R2-D2 and C-3PO to try and free him. Princess Leia - disguised as a bounty hunter - and Chewbacca go along as well. The final battle takes place on the moon of Endor, with its natural inhabitants, the Ewoks, lending a hand to the Rebels. Will Darth Vader and the Dark Side overcome the Rebels and take over the universe?",
      "poster" : "star_wars_episode_6_poster.png",
      "hero_image" : "star_wars_episode_6_hero.jpg"
    }];


The Movie class looks like this:

export class Movie {
  title: string;
  episode_number: string;
  main_characters: string[];
  description: string;
  poster: string;
  hero_image: string;
}

Now we can test out these predicate methods FirstOrDefault and Where ! Note that in case you have worked with C# and Linq before, the syntax and the Intellisense will make you feel more at home with this kind of code - not only do you have type checking, but you can also pass in strongly typed predicate methods using Typescript, Generics and the Array prototype techniques discussed here. Also note how I define a predicate type here defining an arrow function, being the predicate.

import { Component, Inject } from '@angular/core';
import { StarWarsMovies } from './starwarsmovies';
import { Movie } from './movie';
import './array-extensions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Linq TsExtensions demo';

  firstMovieWithBoba: Movie;
  allMoviesWithLeia: Movie[];
  starwarsMovies: string;

  constructor() {
    this.starwarsMovies = JSON.stringify(StarWarsMovies);
    this.firstMovieWithBoba = 
StarWarsMovies.FirstOrDefault<Movie>(m => m.main_characters.indexOf('Boba Fett') > 0);
    this.allMoviesWithLeia = 
StarWarsMovies.Where<Movie>(m => m.main_characters.indexOf('Princess Leia Organa') > 0);
    console.log(this.firstMovieWithBoba);
    console.log(this.allMoviesWithLeia);
  }

}


We now can work on our strongly typed arrays and define easily our filtering predicates for Where and FirstOrDefault implementation. It should be easy to extend this into more methods, such as SingleOrDefault and Any and so on. The screen shot below shows the results after running ng serve -o:

Friday 28 December 2018

Debugging http Node.js server requests and responses from shell

Here is a cool tip to inspect Node.js running an http process / server. First off, we start a simple http server in Node.js like this:

const http = require('http');

const server = http.createServer((req, res) => {
    res.end("

Why hello world!

\n"); }); server.listen(4242, () => { console.log("Server is running..."); });
This sets up a http server in Node.js. The http module is built-in in Node.js, we only have to require it (import in ES6). Now, just enter the following (I use Windows Subsystem for Linux - WSL in this case) in your shell to export NODE_DEBUG environment variable: export NODE_DEBUG=http We can now see requests and responses in our Node.js server! (Image below uses Wsl-Terminal as our terminal against WSL).

Sunday 23 December 2018

Canceling Promise in Javascript

Canceling Promise in Js is a often sough after functionality, that can be provided by wrapping the Promise async function and provide canceling abilities. This will in functionality be similar to what we can do in C# with a CancellationTokenSource using in System.Threading.Task objects. We can invoke asynchronous function in Js with Promise, but if the user navigates away from a View or Page in for example React Native component, clicking a button to go to another Component, we must tidy up already started Promise operations such as fetch and here is the code to achieve that. First off, we define and export a makeCancelable method to be able to cancel a Promise.

/**
 * Wraps a promise into a cancelable promise, allowing it to be canceled. Useful in scenarios such as navigating away from a view or page and a fetch is already started.
 * @param {Promise}   promise           Promise object to cancel.
 * @return {Object with wrapped promise and a cancel function}
 */
export const makeCancelable = (promise) => {
    let hasCanceled = false;

    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then(value => hasCanceled ? reject({ isCanceled: true }) : resolve(value),
            error => hasCanceled ? reject({ isCanceled: true }) : reject(error)
        );
    });

    return {
        promise: wrappedPromise,
        cancel() {
            hasCanceled: true;
        }
    };

};

The promise is wrapped with additional logic to check a boolean flag in a variable hasCanceled that either rejects the Promise if it is canceled or resolves the Promise (fullfils the async operation). Returned is an object in Js with the Promise itself in a primise attribute and the function cancel() which sets the boolean flag hasCanceled to true, effectively rejecting the Promise and rejecting it. Example usage below:

'use strict';

import React, { Component } from 'react';
import { TextInput, Text, View, StyleSheet, Image, TouchableHighlight, ActivityIndicator, FlatList, AsyncStorage } from 'react-native';
import AuthService from './AuthService';
import { makeCancelable } from './Util';

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#F5FCFF',
        flex: 1,
        paddingTop: 40,
        alignItems: 'center'
    },
    heading: {
        fontSize: 30,
        fontWeight: '100',
        marginTop: 20
    },
    input: {
        height: 50,
        marginTop: 10,
        padding: 4,
        margin: 2,
        alignSelf: 'stretch',
        fontSize: 18,
        borderWidth: 1,
        borderColor: '#48bbec'
    },
    button: {
        height: 50,
        backgroundColor: '#48bbec',
        alignSelf: 'stretch',
        marginTop: 10,
        justifyContent: 'center'
    },
    buttonText: {
        fontSize: 22,
        color: '#FFF',
        alignSelf: 'center'
    },
    error: {
        fontWeight: '300',
        fontSize: 20,
        color: 'red',
        paddingTop: 10
    }
});

const cancelableSearchRepositoriesPromiseFetch = makeCancelable(fetch('https://api.github.com/search/repositories?q=react'));

class LoginForm extends Component {

    constructor(props) {
        super(props);

        this.state = {
            showProgress: false,
            username: '',
            password: '',
            repos: [],
            badCredentials: false,
            unknownError: false,
        };


    }

    onLoginPressed() {
        this.setState({ showProgress: true });

        var reposFound = [];

        var authService = new AuthService();

        authService.login({
            username: this.state.username, password: this.state.password
        }, (results) => {
            this.setState(Object.assign({ showProgress: false }, results));

            if (this.state.success && this.props.onLogin) {
                this.props.onLogin();
            }

        });




        cancelableSearchRepositoriesPromiseFetch.promise.then((response) => { return response.json(); })
            .then((results) => {
                results.items.forEach(item => {
                    reposFound.push(item);
                });
                this.setState({ repos: reposFound, showProgress: false });
            });
    }

    componentWillMount() {
        this._retrieveLastCredentials();
        cancelableSearchRepositoriesPromiseFetch.cancel();
    }

    _retrieveLastCredentials = async () => {

        var lastusername = await AsyncStorage.getItem("GithubDemo:LastUsername");
        var lastpassword = await AsyncStorage.getItem("GithubDemo:LastPassword");
        this.setState({ username: lastusername, password: lastpassword });
    }

    _saveLastUsername = async (username) => {
        if (username != null) {
            await AsyncStorage.setItem("GithubDemo:LastUsername", username);
        }
    }

    _savePassword = async (password) => {
        if (password != null) {
            await AsyncStorage.setItem("GithubDemo:LastPassword", password);
        }
    }

    componentWillUnmount() {

    }

    render() {


        var errorCtrl = <View />;

        if (!this.state.success && this.state.badCredentials) {
            errorCtrl = <Text color='#FF0000' style={styles.error}>That username and password combination did not work</Text>
        }

        if (!this.state.success && this.state.unknownError) {
            errorCtrl = <Text color='#FF0000' style={styles.error}>Unexpected error while logging in. Try again later</Text>
        }

        return (
            <View style={styles.container}>
                <Image style={{ width: 66, height: 55 }} source={require('./assets/Octocat.png')} />
                <Text style={styles.heading}>Github browser</Text>
                <TextInput value={this.state.username} onChangeText={(text) => { this._saveLastUsername(text); this.setState({ username: text }); }} style={styles.input} placeholder='Github username' />
                <TextInput value={this.state.password} textContentType={'password'} multiline={false} secureTextEntry={true} onChangeText={(text) => { this._savePassword(text); this.setState({ password: text }); }} style={styles.input} placeholder='Github password' />
                <TouchableHighlight style={styles.button} onPress={this.onLoginPressed.bind(this)}>
                    <Text style={styles.buttonText}>Log in</Text>
                </TouchableHighlight>
                {errorCtrl}
                <ActivityIndicator animating={this.state.showProgress} size={"large"} />
                <FlatList keyExtractor={item => item.id} data={this.state.repos} renderItem={({ item }) => <Text>{item.full_name}</Text>} />

            </View>
        );

    }


}

export default LoginForm;