5
\$\begingroup\$

I have the following code to get data using async/await

import fetch from 'node-fetch';

let bid = 0;
let ask = 0;
let lastBuy = 0;
let buyOrders = [];
let sellOrders = [];
const currency = process.argv[3].toUpperCase();

const getOrderBook = async () => {
  const orderBook = await fetch(`https://bittrex.com/api/v1.1/public/getorderbook?market=BTC-${currency}&type=both`)
    .then(response => response.json())
    .then(response => response.result)
    .then(result => ({ buys: result.buy.slice(0, 10), sells: result.sell.slice(0, 10) }));

  return orderBook;
};

const getTicker = async () => {
  const ticker = await fetch(`https://bittrex.com/api/v1.1/public/getticker?market=BTC-${currency}`)
    .then(response => response.json())
    .then(response => response.result);

  return ticker;
};

const getCurrencyData = () => {
  getOrderBook().then(orderBook => ([buyOrders, sellOrders] =  [orderBook.buys, orderBook.sells] )); //eslint-disable-line
  getTicker().then(ticker => ([bid, ask, lastBuy] = [ticker.Bid, ticker.Ask, ticker.Last])); //eslint-disable-line
};


setInterval(() => {
  getCurrencyData();
}, 5000);

This is the first time I use async/await, so I have the following doubts:

First, on my getOrderBook and getTicker function I can't find a way to return the values without first assigning to a variable.

Also, it looks very ugly in the getCurrencyData function, because after I called the functions I have to make a ".then" to assign the data to my variables.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Polling live data

I am taking the code at face value. If you are pushing data forward then this answer does not apply.

By the looks of it you are not pushing the data forward after you fetch it, which means you don't need to know when the fetch cycle is complete. This allows for a few short-cuts.

Some points first.

  • Move the URLs to the top as constants.
  • Separate out the API address
  • Collect the data bid, ask, last, ... etc in a single object.
  • Use a single async function and pass the URL and data destination to it.
  • Encapsulate your state and expose only the states required,

Ignoring the promise

As you don't seam to be doing anything with the fetched data, you can ignore the Promise returned by the async function.

An async function returns a Promise which can not be avoided, but you can side step the promise and assign the resulting object inside the function.

const bitcoinFeed = (() => {
    const stream = {ticker: {},  orderBook: {}}
    const interval = 5000;
    const currency = process.argv[3].toUpperCase();
    const API = "https://bittrex.com/api/v1.1/public/";
    const orderURL = `getorderbook?market=BTC-${currency}&type=both`;
    const tickerURL = `getticker?market=BTC-${currency}`;

    // The function assigns the resulting object to data so you can ignore
    // the returned promise.
    async function getData(url, data) {
         Object.assign(data, (await (await fetch(API + url)).json()).result);
    }

    // fetch data every 5 sec
    (function updateData() {
        getData(tickerURL, stream.ticker);
        getData(orderURL , stream.orderBook);
        setTimeout(updateData, interval);
    })()

    // return the object containing live data
    return stream;
 })();

Getters make life easy.

With a few modifications you can return an object containing getters

const bitcoinFeed = (() => {
    const ticker = {};
    const orderBook = {};
    const interval = 5000;
    const currency = process.argv[3].toUpperCase();
    const API = "https://bittrex.com/api/v1.1/public/";
    const orderURL = `getorderbook?market=BTC-${currency}&type=both`;
    const tickerURL = `getticker?market=BTC-${currency}`;
    const getData = async (url, data) => 
        Object.assign(data, (await (await fetch(API + url)).json()).result);

    ;(function updateData() {
        getData(tickerURL, ticker);
        getData(orderURL , orderBook);
        setTimeout(updateData, interval);
    })()

    // return the object containing live data
    return {
        get bid() { return ticker.Bid },
        get ask() { return ticker.Ask },
        get last() { return ticker.Last },
        get sells() { return orderBook.sells.slice(0, 10) },
        get buys() { return orderBook.buys.slice(0, 10) },
    };
 })();

All the data wrapped in a neat object and not a then in sight.

\$\endgroup\$
5
\$\begingroup\$

One of the main benefits of using await is to keep your code flat and to avoid .thens - you're not doing that in getOrderBook. nor in getTicker. If you want to use await, try awaiting each asynchronous operation.

Accessing a property of an object is not an asynchronous operation, nor is creating a new object: those lines should not be in .thens.

Because both fetch() and .json() access the .result property, you can keep from repeating yourself by putting that into another function:

const fetchBittrexResult = async (method) => {
  const params = 'market=BTC-' + currency + (method === 'getorderbook' ? '&type=both' : '');
  const url = `https://bittrex.com/api/v1.1/public/${method}?${params}`;
  const response = await fetch(url);
  const responseObj = await response.json();
  return responseObj.result;
};

Then consume it:

const getOrderBook = async () => {
  const { buy, sell } = await fetchBittrexResult('getorderbook');
  return {
    buys: buy.slice(0, 10),
    sells: sell.slice(0, 10),
  };
};

const getTicker = () => fetchBittrexResult('getticker');

(no need for getTicker to be async anymore)

For getCurrencyData, I'm doubtful that continually re-assigning top-level variables is a good way to do this, but without seeing the consumers of the fetched data, it's hard to say what the better solution is. Argument destructuring will help reduce the syntax noise there, and don't feel like you have to squash everything onto one long line:

const getCurrencyData = () => {
  getOrderBook()
    .then(({ buys, sells }) => ([buyOrders, sellOrders] =  [buys, sells] ));
  getTicker()
    .then(({ Bid, Ask, Last }) => ([bid, ask, lastBuy] = [Bid, Ask, Last]));
};

It would probably be better to assign to an object rather than to multiple standalone variables, though.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.