JavaScript Async Fun - Flow Control

Github Repo

Made by nem035

The Problem

Callbacks Solution

Render

Received

file1
file2
file3
// map containing contents of each file
const contents = {
  file1: undefined,
  file2: undefined,
  file3: undefined
};

// map containing a flag indicating if file is rendered
const rendered = {
  file1: false,
  file2: false,
  file3: false
};

// concurrent ("parallel") requests
getFile('file1');
getFile('file2');
getFile('file3');

function getFile(file) {
  getFromServer(file, function (response) {

    // store the current file response
    contents[file] = response;

    // go through all the files
    // for each file, in order,
    // if it wasn't received
    // stop the loop
    // otherwise continue looping
    // and render unrendered files
    const allRendered = ['file1', 'file2', 'file3'].every(function(file) {
      if (contents[file]) {
        if (!rendered[file]) {
          render(contents[file]);
          rendered[file] = true;
        }
        return true;
      } else {
        return false;
      }
    });

    if (allRendered) {
      console.log('Complete!');
    }
  });
}

function render(contents) {
  // render contents to DOM
}

Thunks Solution

Render

Received

file1
file2
file3
// concurrent ("parallel") requests
const thunk1 = getFile('file1');
const thunk2 = getFile('file2');
const thunk3 = getFile('file3');

// sequential rendering
thunk1(function(contents1) {
  render(contents1);
  thunk2(function(contents2) {
    render(contents2);
    thunk3(function(contents3) {
      render(contents3);
      console.log('Complete!')
    });
  });
});

// A thunk maker that caches the
// callback or the response,
// depending on which is obtained
// first and executes the callback
// with the response, once both are
// received
function getFile(file) {
  let contents;
  let callback;
  getFromServer(file, function(response) {
    if (callback === undefined) {
      contents = response;
    } else {
      callback(response);
    }
  });
  return function(cb) {
    if (contents === undefined) {
      callback = cb;
    } else {
      cb(contents);
    }
  };
}

function render(contents) {
  // render contents to DOM
}

Promises Solution

Render

Received

file1
file2
file3
// concurrent ("parallel") requests
const promise1 = getFile('file1');
const promise2 = getFile('file2');
const promise3 = getFile('file3');

// sequential (chain) rendering
promise1
.then(render)
.then(function() {
  return promise2;
})
.then(render)
.then(function() {
  return promise3;
})
.then(render)
.then(function() {
  console.log('Complete!');
});

// A promise maker
function getFile(file) {
  return new Promise(function(resolve) {
    getFromServer(file, resolve);
  });
}

function render(contents) {
  // render contents to DOM
}

Generators Solution

Render

Received

file1
file2
file3
runner(gen());

// A generator
function* gen() {

  // concurrent ("parallel") requests
  const promise1 = getFile('file1');
  const promise2 = getFile('file2');
  const promise3 = getFile('file3');

  // sequential renders
  render(yield promise1);
  render(yield promise2);
  render(yield promise3);

  console.log('Complete!');
}

// A (recursive) generator runner
function runner(iter, prev) {
  let promise = iter.next(prev).value;
  if (promise) {
    promise.then(function(file) {
      runner(iter, file);
    });
  }
}

// A promise maker
function getFile(file) {
  return new Promise(function(resolve) {
    getFromServer(file, resolve);
  });
}

function render(contents) {
  // render contents to DOM
}