saga-take

take, fork

by chaofeng liu

HTML

<button id="test">
  Clickme
</button>
<div id="result">
</div>

JavaScript

const $btn = document.querySelector('#test');
const $result = document.querySelector('#result');

function channel() {
  let taker;

  function take(cb) {
    taker = cb;
  }

  function put(input) {
    if (taker) {
      const tempTaker = taker;
      taker = null;
      tempTaker(input);
    }
  }

  return {
    put,
    take,
  };
}

const chan = channel();

let i = 0;
$btn.addEventListener('click', () => {
  chan.put(`action data${i++}`);
}, false);

function take() {
  return {
    type: 'take'
  };
}

function fork(cb) {
  return {
    type: 'fork',
    fn: cb,
  };
}

function* takeEvery(worker) {
  yield fork(function* () {
    while(true) {
      const action = yield take();
      worker(action);
    }
  });
}

function* mainSaga() {
  yield takeEvery(action => {
    $result.innerHTML = action;
  });
}

function runTakeEffect(effect, cb) {
  chan.take(input => {
    cb(input);
  });
}

function runForkEffect(effect, cb) {
  task(effect.fn || effect);
  cb();
}

function task(iterator) {
  const iter = typeof iterator === 'function' ? iterator() : iterator;
  function next(args) {
    const result = iter.next(args);
    if (!result.done) {
      const data = result.value;

      if (typeof data[Symbol.iterator] === 'function') {
        runForkEffect(data, next);
      } else if (data.type) {
        switch (data.type) {
        case 'take':
          runTakeEffect(result.value, next);
          break;
        case 'fork':
          runForkEffect(result.value, next);
          break;
        default:
        }
      }
    }
  }
  next();
}

task(mainSaga);