JSFiddle - React, Tailwind, and code Playground

HTML

<div id="root" class="container">
  <div id="loader" class="loader">
    <img class="loader-img" src='https://i.imgur.com/YwRCROv.gif'>
  </div>
</div>

CSS

.container {
  height: 300px;
  width: 60%;
  border: 1px solid #d3d3d3;
  overflow: auto;
  margin: 40px;
  background-color: #F6F7F9;
  border-radius: 5px;
}

.item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  background-color: #dbdbdb;
  margin: 10px;
  float: left;
  border-radius: 5px;
  box-shadow: 1px 5px 8px 0px rgba(47,57,71,0.06);
}

.loader {
  display: flex;
  width: 100%;
  height: 100px;
  align-items: center;
  justify-content: center;
}

.loader-img {
  width: 80px;
  height: 35px;
}

JavaScript 1.7

function getContent(offset, num) {
  let arr = [];
  for (var i = offset; i < offset + num; i++) {
    arr.push(i);
  }

  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(arr);
    }, Math.random() * 2000);
  });
}

/**
	Returns a generator which returns the items as a stream
*/
async function* items(batchSize) {
  let offset = 0;
  while (true) {
    yield await getContent(offset, batchSize);
    offset += batchSize;
  }
}

async function* events(el, name, condition) {
  let resolve;
  el.addEventListener(name, e => {
    if (condition(e)) {
      resolve(e);
    }
  });
  while (true) {
    yield await new Promise(_resolve => resolve = _resolve);
  }
}

const container = document.getElementById('root');
const loader = document.getElementById('loader');

async function init() {
 
 let eventIterator = events(container, 'scroll', () =>container.scrollHeight <= container.scrollTop + container.clientHeight);
  
  for await (const page of items(50)) {
  
	 append(page);
  debugger;
  await eventIterator.next();
  }
}

function append(items) {
  let dom = items.map(item => `<div class=item>${item}</div>`).join('');
  container.insertAdjacentHTML('beforeend', dom);
  container.append(loader);
}

init();