Promises feedback

hello all - first time posting here.

i would like somebody else knowledgeable with promises to have a look at my code example & see if this is correct or not.

the code is attempting to simulate three tasks and share parameters. this will be my working example for future projects.

thank you very much for your consideration.

const firstPromise = (promiseInput) => {
    return new Promise( (resolve, reject) =>  {          
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into first promise. '  ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
		// we could use the "reject" function if this promise failed!
    })
}

const secondPromise = (promiseInput) => {
    return new Promise( (resolve, reject) =>  {          
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into second promise. ' ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
    })
}

const thirdPromise = (promiseInput) => {
    return new Promise( (resolve, reject) =>  {          
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into third promise. ' ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
    })
}


firstPromise('one')
  .then(  value => { return secondPromise (value + ' two')    })
  .then(  value => { return thirdPromise  (value + ' three')  })
  .then(  value => { console.log(' FINAL result: ' + value)   })
  .catch( error => { console.log(' ERROR! ' + error )         })
  ;

In modern JavaScript chaining asynchronous functions can be more elegantly written using arrow functions.

.then(v => firstPromise())

Furthermore, my professional preference is to simplify function argument names when scope is limited.

Also when functions just return promises they can more elegantly be declared as an arrow function.

const func = () => new Promise(…)

As of late I also prefer to make function input less restrictive using destructuring instead.

const func = ({ param }) => new Promise(…)
func({ param: 123 }).then(v => firstPromise())

This approach enables the function signature to more easily change over time with less negative impact to existing code.

Using these approaches religiously will increase coding efficiency. Some might just see them as syntactic sugar but I see them as development workflow optimizations. We have been giving these powerful features in modern JavaScript might as well use them to our full advantage.

Programming in this way using asynchronous chains also sets the stage for more advanced methodologies like functional reactive coding. In contrast to more traditional procedural object oriented restricted languages.

Applications running in a browser are asynchronous by nature. Therefore, it makes more sense to adapt programming methodologies like functional reactive programming suited to that rather than those used in synchronous environments such as a server.

2 Likes

thank you very much windbeathmywings for your excellent suggestions.

question: when you suggest:

const func = ({ param }) => new Promise(…)
func({ param: 123 }).then(v => firstPromise())

i assume you are passing in a JSON object? i had forgotten that:

({ param })

does that, I saw it somewhere. I have used that before once, recently in node trying to send in a half-dozen parameters got messed up in the function, so i used this method. it’s weird that Node was having trouble with parameters.

shortcuts make the code more readable for sure, although oftentimes noobs like myself start to use them and forget that it’s really a shortcut. IMHO i try to learn the LONG way first, then the shortcuts, always keeping in mind that i am using a shortcut.

here are your implemented suggestions:

const firstPromise = (promiseInput) => {
    return new Promise( (resolve, reject) =>  {          // long version
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into first promise. '  ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
		// we could use the "reject" function if this promise failed!
    })
}
                               // shorter version per "windbeneathmywings"
const secondPromise = (promiseInput) => new Promise( (resolve, reject) =>  {          
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into second promise. ' ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
})

const thirdPromise = (promiseInput) => new Promise( (resolve, reject) =>  {          
      console.log(promiseInput);
      let returnStuff = promiseInput + ' - parameter passed into third promise. ' ;
      setTimeout( () => {
          console.log ('waiting a half-second, resolving: ' + returnStuff);
          resolve  (returnStuff);
      },500 );
})

firstPromise('one')
  .then(  value => { return secondPromise (value + ' two')    })  // long version
  .then(  value =>          thirdPromise  (value + ' three')   )  // shorter version per "windbeneathmywings"
  .then(  value =>          console.log(' FINAL result: ' + value)   )
  .catch( error => { console.log(' ERROR! ' + error )         })
  ;

That is parameter context matching, to destructure only wanted parts from the object.
Further details can be found at http://es6-features.org/#ParameterContextMatching

In that example, {param: 123} is the object being given as an argument to the func function.

1 Like

Paul_Wilkins - you are too fast for me! :grinning:

i was playing with it and came up with something like this as another example:

function tester({param}) {
    console.log(JSON.stringify(param));
}
tester({param: { 'parmOne': 123, 'paramTwo': "two"}}) ;

first i want to get a good working example of the promises, then have an example of the “destructuring” concept separate from my promises example. but i can see the value of destructuring, and i will need to go back and include it in my last project. too bad i didn’t learn this a month ago.

1 Like

Here is a working through why the destructuring has been so helpful.

Say for example that you have a car array, with lots of information stored in objects:

const car = [
  {make: "Ford" model: "Model T", transmission: "2-speed planetary gear", hp: 14},
  {make: "Volkswagon", model: "Beetle", transmission: "4-speed manual", hp: 25},
  {make: "Toyota", model: "Corolla", transmission: "5-speed manual", hp: 165 }
};

I normally tend to use a plural for arrays, but it is common for people to use singular terms for arrays so it’s useful to cater for that type of use too.

When dealing with info from the object, such as to filter them in some way, you would typically need to avoid giving function parameters that are the same as the name of the array. When it’s a singular name being used for the array, that typically means giving the function parameter a different name of eachCar, or something more generic such as item or obj.

When retrieving info from that object, we tend to extract info from that object before use:

function desiredCar(eachCar) {
  const make = eachCar.make;
  const model = eachCar.model;
  const hp = eachCar.hp;
  // then do stuff with make, model, and hp
  ...
}
const filteredCars = car.filter(desiredCar);

It is those first statements that extract information from the object, where the benefit of destructuring is gained.

// function desiredCar(eachCar) {
function desiredCar({make, model, hp}) {
  // const make = eachCar.make;
  // const model = eachCar.model;
  // const hp = eachCar.hp;
  // then do stuff with make, model, and hp
  ...
}
const filteredCars = car.filter(desiredCar);

That way you end up with more condensed code, that still makes a lot of sense.

function desiredCar({make, model, hp}) {
  // do stuff with make, model, and hp
  ...
}
const filteredCars = car.filter(desiredCar);
1 Like

Going to possibly confuse matters here, sorry.

Looking at your example code, the promise functions are all very similar and the overall process seems to be concatenation.

If we take promises out of the equation for the moment, we can use reduce to achieve something similar.

['one', 'two', 'three']
  .reduce((accumulator, currValue) => accumulator, currValue, '')
// outputs -> onetwothree

I wondered how the same could be achieved with promises and came up with the following reducer function.

const sumAsync = (accumulatedPromise, currentValue) => {
  return accumulatedPromise.then(
    (accumulatedValue) => 
      new Promise((resolve) =>
        setTimeout(
          () => resolve(accumulatedValue + currentValue), 500
        )
      )
  )
}

If we use await I think the code is more readable.

const sumAsync = async(accumulatedPromise, currentValue) => {
  const accumulatedValue = await accumulatedPromise
  
  console.log(accumulatedValue)
  return new Promise(
    (resolve) => 
      setTimeout(() => resolve(accumulatedValue + currentValue), 500
    )
  )
}

It can then be used like this

const numberStrings = ['one', 'two', 'three', 'four']
const resultString = numberStrings.reduce(sumAsync, Promise.resolve(''))

// Outputs every 500ms
one
onetwo
onetwothree
onetwothreefour

It maybe that your actual promise functions are all very different, but much like with a pipe function, I think reduce could be used to process the promises in sequence outputting a final result.

Just to add @windbeneathmywings suggestion of reactive coding, in particular rxJS, may be worth looking at.

hmmm i gotta think about this one. (dot)filter is a new one. it looks very powerful though!

although i get the idea that you are really just setting up some sort of template - cool stuff indeed.

rpg_digital - good information, although i am planning on moving my node project from sequelize to knex which is entirely promises. this example is really just to make promises a bit easier for us old dinosaur types to understand.

i did get a working example of this from https://stackoverflow.com/questions/70883305/best-way-to-make-a-knex-request-from-inside-a-promise but the person who replied left out parameter passing.

double-learning-curve: Knex and Promises (NO not touching typescript and deno anytime soon)

1 Like

Well as a fellow dinosaur, I appreciate that :slight_smile:

Oh yes. The array methods have advanced over recent years by leaps and bounds.

ES5 introduced many improvements, you can find a good intro to things like array.forEach, array.filter, array.map, and array.reduce along with many other features at https://ducmanhphan.github.io/2019-02-28-New-features-in-ES5/

oh sorry, i did not mean to offend the dinosaurs. i meant to say “fellow Trilobites”.

1 Like

oh gee thanks Paul - now i gotta rewrite all my node code from the last couple of months…! (sorry i could not resist that one)

so would you describe your approach as setting up a “template” for parameters? it reminds me a bit of c++ prototyping if that’s the case.

RxJS brings asynchronous programming to a whole new level. Being able to easily chain async operations and handle data as streams is very powerful. Not to mention the knowledge directly translates to several other languages where reactive programming can be implemented. This include entire database alternative storages like Kafka for event driving programming.

Talking about databases though RxDB is a project I’ve been following for a while. This is a database that can be used in the browser and also synced with remote storages. One really neat aspect to point out is that there are a lot of different storage options supported via different adaptors.

hey Paul -

while playing with your code:

function desiredCar(eachCar) {
  const make = eachCar.make;
  const model = eachCar.model;
  const hp = eachCar.hp;
  // then do stuff with make, model, and hp
  ...
}
const filteredCars = car.filter(desiredCar);

filteredCars is just an empty array. desiredCar(eachCar) is receiving an array? in order to see the entire array, i had to so something like:

function desiredCar(eachCar) {
   console.log(JSON.stringify(eachCar));
  const make = eachCar.make;       // not working since something like eachCar[0].make required.
  const model = eachCar.model;    // same
  const hp = eachCar.hp;               // same
  eachCar.forEach( element =>  console.log( `Make: ${element.make}  Model: ${element.model} Horsepower: ${element.hp}`));
  // display the parameter(s) for testing purposes
}

i am not quite clear how desiredCar is doing any sort of filtering. could i please ask you to expand your example a bit?

i think i see where you are going, but not sure. example, this returns the ford:

JSON.stringify(cars.filter(index => index.make == 'Ford' ));

also,

const car = [
{make: “Ford”, model: “Model T”, transmission: “2-speed planetary gear”, hp: 14},
{make: “Volkswagon”, model: “Beetle”, transmission: “4-speed manual”, hp: 25},
{make: “Toyota”, model: “Corolla”, transmission: “5-speed manual”, hp: 165 }
];

last character is a bracket, not a curly-brace, right? :grin: and "Ford" model: is missing the comma, clearly this is some sort of test to separate the dinosaurs from the trilobites. did i pass the test?? :joy::rofl:

IMHO, most of my DB work i have done includes tables with multiple parent-tables, hence, strictly enforced referential integrity. i have yet to see a pure NoSQL do this.

well truth-be-told, i have only looked closely at mongoDb and rethink.

but that being said, MariaDB now includes NoSQL structuring, so its very easy to have something of a “hybrid” table that includes “dependent” keys from parent tables, a unique ID key, perhaps a couple of required columns, and the rest of the table is like a JSON object.

it seems to me that MariaDB could be classified as a NewSQL, but i am sure nobody is going to agree with me on that!

The looping callback has three parameters of (item, index, array). That third parameter always refers to the full array that you are looping through.

It’s deliberately missing because it is the last item in the list. Dangling commas are to be avoided and excised in my style guide.

You’ve done well :slight_smile: I’ll have to search out my stock of gold stars, but in the meantime have this virtual chocolate fish.

1 Like

Mongo has features specifically available for managing and querying hierarchical data.

Stuffing json into a database that has high latency, low scalability, poor performance, large learning curve doesn’t seem very responsible. That is when the same data could be stored in a low latency, high scalability, performant, low learning curve relational alternative instead. There is a mountain of options available for storing json documents better suited than a relational database.

My professional choice is actually a CDN. Terabytes of json files can be stored on a CDN for free. Furthermore, a CDN by nature is designed to be highly available, scalable, and performant. In the case of an AWS CDN files can be written securely directly in the browser eliminating the need for a database and server entirely. An entire dynamic web application can run from a CDN and write json files for free without sacrifice of latency, availability, scalability, and performance. The application will actually have significant performance boosts in comparison to apps relying on a server or traditional database.

MySQL supports stuffing json into columns and has functions to query the data. Again though not something I would recommend at scale.

actually you originally specified:

{make: “Ford” model: “Model T”, transmission: “2-speed planetary gear”, hp: 14},

and i think you meant:

{make: “Ford” , model: “Model T”, transmission: “2-speed planetary gear”, hp: 14},

so "Ford" model: became "Ford" , model:

missing comma. good catch, Marky!

ok seriously now, i THINK this is what you are saying, something like this (?):

function desiredCar(eachCar, indexNbr, entireArray) {
     
     console.log('eachCar: '       + JSON.stringify(eachCar));
     console.log('indexNbr: '      + indexNbr);
     console.log('entireArray: '   + JSON.stringify(entireArray));
 
   if  ( eachCar.make == 'Ford' ) {
         return true;
   } else  {
         return false;
   }
 }
 console.log('Ford final result: ' + JSON.stringify(cars.filter(desiredCar)));

please let me know if i am anywhere close. i also tried playing around with passing “Ford” into the filter but could not figure that out.

and thank you for your patience. the brain seems to be stuck in neutral this week.

1 Like

Are you actually saying it’s faster to parse and search through terabytes of JSON than to use a MySQL database?