Promises for Rubyists
Tags: javascript, ruby
Published: 2020-12-07 10:57:42

# The Soundtrack

I tried to write a post about JavaScript Promises using Ciara - Promise, but it didn't work so instead have this genre-agnostic playlist of 10 songs with the title Promise. Don't let my hard work be in vain & have a listen.
{% spotify spotify:playlist:6mvCLTreLdT2gBMTwUeTKJ %}

# The Background
JavaScript and Ruby are both single-threaded programming languages, they can really only do one thing at a time, in a particular order. This also means they're both synchronous. They run in a queue-like way, the first operation or function to get called is the first to be performed before any other operation is performed, this presents a problem the moment you want to do anything that requires multi-tasking. Both languages have workarounds, modules, gems and in-built features that can allow you to write asynchronous code, e.g. JavaScript's [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) or background jobs in Ruby. JavaScript also has promises, the topic of today, which Ruby doesn't have an in-built match for at the moment, so I'm going to try my best to recreate what this could look like.

# The Promise

It's a commitment to give you _something_ later, it'll either be the thing you ask for or an error but you'll definitely get something.

>Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.

-https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

### Callbacks in Ruby
In Ruby, we really mainly deal with callbacks in the context of Rails (or other web framework) when we're manipulating objects during their creation phase. You might have used a `before_create: :do_thing` in a model, this is generally what callbacks are in Rails (not necessarily Ruby), and there are a [list of them](https://guides.rubyonrails.org/active_record_callbacks.html). But using plain old Ruby, you'd have a method that you could pass a block to:

```ruby
  def add_one(n)
     total = n + 1
     yield total
  end

  add_one(5) { |total|
    puts "the total is #{total}"
  }
```
The callback here is the block we pass to `add_one(5)` which is then called with our `yield` in the definition of the method. So here we're passing the callback to the method.

### Callbacks in JavaScript

Unlike Ruby, JavaScript functions can accept functions as arguments but not blocks which means you'd create dedicated callback functions in a JS context.

```javascript
  function getTotal(t) {
    return "the total is ${total}"
  }

  function addOne(n, callback) {
    const t = n + 1;
    callback(t);
  }

  addOne(5, getTotal);
```
Here we're also passing the callback to the function, similar to the Ruby implementation. This is synchronous since a blocking operation (the `addOne`) needs to happen first before the callback can happen.

# Implementation

There isn't a native way to write promises in Ruby but just to illustrate the functionality, imagine being able to send data between your controller and view without refreshing the page in Ruby, with no JavaScript. It's the stuff dreams are made of but in the real world we need JavaScript.

I've been working on the [Samsung's Global Goals](https://medium.com/samsung-internet-dev/samsung-the-global-goals-web-debuts-f8cdae4ec21d) PWA and in this I've had to use promises to interact with Stripe and the Payment Request API. Let's see a real world example of this:

```javascript

  async function fetchPaymentIntentClientSecret(amount){
    const fetchedPaymentIntentCS = await fetch(`/fetchPaymentIntent/${amount}`);
    const clientSecretObj = await fetchedPaymentIntentCS.json();

    return clientSecretObj.clientSecret;
}

 fetchPaymentIntentClientSecret(amount).then((clientSecret) => {
        confirmPayment(paymentRequest, clientSecret);
    }).catch((err) => {
        console.log(err);
    });
```

The `fetchPaymentIntentClientSecret` function is defined using the keyword `async`, in the function we make a call to the server using `await` and `fetch` this call then gives us back some data which we return. The `async` and `await` functions are important here:

>The `async` and `await` keywords enable asynchronous, promise-based behaviour to be written in a asynchronous style, avoiding the need to explicitly configure promise chains.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

The function could also be written like this:

```javascript
    function fetchPaymentIntentClientSecret(amount){
       return fetch(`/fetchPaymentIntent/${amount}`)
		.then(response => response.json()) // response.json also returns a promise since it has to wait for the response to finish before it can parse it
		.then(clientSecretObj => clientSecretObj.clientSecret); // extract the thing you need from the response
}
const fetchPaymentIntentCSPromise = fetchPaymentIntentClientSecret(amount)
	.then(clientSecret => confirmPayment(paymentRequest, clientSecret));
```

This means that `fetchPaymentIntentClientSecret` actually returns a promise. `async` and `await` are just syntactic sugar for the promises syntax. Using these keywords together, along with `fetch` allows us to make the asynchronous call to the server. So when we actually call the function, because it's a promise, we can chain the callbacks and really take advantage of the asynchronous nature. The `clientSecret` is returned from the server and we can pass that to the next function that needs it if the call is successful and if it's not, we can log the error instead.

All without the page being refreshed or modified.

### A Note

You might have seen promise syntax that looks like

```javascript
   function myFancyFunc() {
      // does something
   }

  const myFancyFuncPromise = new Promise(myFancyFunc)
```
and you're wondering why I haven't done that here. Well, the syntax is different if you're working with a promise-based API, which I am. In our example `fetch` returns a promise as does `response.json` so we need to treat them as such. `new Promise` is used to make promises out of async APIs which are not promise based, e.g. the callback based functions we defined earlier.

# Why?

Within the context web development, promises are unique to JavaScript in that they're native. Coming from a Ruby background I found them strange, why not just do these things in a background job? But honestly, a small action like retrieving a client secret doesn't need to be done in a job (and probably shouldn't be) and it's probably not the best user experience to reload the page just to get a client secret, especially if the user hasn't triggered it.

Promises can also be quite complex to get your head around, this post is a primer but I'd encourage you to read more:

- (Using Promises)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises]
- (Promise)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise]