Пристегнитесь, миллениалы, пришло время авантюрной истории лихого Javascript из девяностых — во имя великой справедливости!

Давайте посмотрим на механическое чудо промисов и посмотрим, как на самом деле легко (и, несомненно, удовлетворительно) вручную написать интерфейс с подобным синтаксическим сахаром, не используя Promise().

Допустим, мы хотим создать генератор случайных чисел.

И мы также хотели бы знать, нечетное оно или четное. Вот синтаксис, который нам обещали (каламбур):

getRandomNumber(1, 10)
  .thatsOdd(function(number) {
    alert(number + ' is odd!')
  })
  .thatsEven(function(number) {
    alert(number + ' is even!')
  })

Сладкий. Теперь, как можно создать эту функцию getRandomNumber?

Чтобы его скомпилировать, нам нужно начать с этой структуры:

function getRandomNumber(min, max) {
  
  // return the object that exposes our two methods
  return {
    thatsOdd:  function(h) {
      // let each method return this, to allow chaining
      return this;
    },
    thatsEven: function(h) { 
      // let each method return this, to allow chaining
      return this;
    }
  };
}

Возвращаемая область действия предлагает API нашего обещания. Он содержит два метода thatsOdd и thatsEven. Оба метода получают обработчик пользователя, который обрабатывает эту конкретную ситуацию.

Чтобы это работало, нам нужно асинхронно вызвать пользовательский обработчик h, который они передали в thatsOdd или thatsEven. Вот как:

function getRandomNumber(min, max) {
  
  // generate the number and see if it's even
  var number = Math.floor(Math.random() * max) + min; 
  var isEven = number % 2 == 0;
  // define the final handler
  var finalHandler;
  // async call to allow caller to map either handler
  setTimeout(function() {
    finalHandler(number);
  }, 0)
  // return the object that exposes our two methods
  return {
    thatsOdd:  function(h) {
      // map the odd handler that will be called async
      if (!isEven) finalHandler = h;
      // let each method return this, to allow chaining
      return this;
    },
    thatsEven: function(h) { 
      // map the even handler that will be called async
      if (isEven) finalHandler = h;
      // let each method return this, to allow chaining
      return this;
    }
  };
}

Вот и все! Вот jsfiddle.

Конечно, есть много оптимизаций, которые можно сделать, но это суть, которая, надеюсь, объясняет немного о том, как создать API с асинхронной цепочкой вручную без беспорядка.

Теперь проведите потрясающий день за своим компьютером.