-->

How do I make ES6 generators wait for promises, li

2019-05-18 12:05发布

问题:

I've read that generators don't wait for promises. How come this is not the case with generators in redux-saga, and how do I make my own generators wait?

For example, this saga:

takeLatest('FETCH_USER_REQUESTED', function*() {
  const fetchPromise = yield put(fetchUser());
  const user = yield fetchPromise;
  console.log(user)
  yield 1
  console.log(1)
})

will output:

Promise
Object // <= user data fetched asynchronously
1

instead of:

Promise
undefined
1

回答1:

How come this is not the case with generators in redux-saga, and how do I make my own generators wait?

This very popular belief, however generators in itself have no relation to Promises or asynchronous functions. Generators is just about make interruptible function with delegating some resources and responsibility to upper level function.

In case of redux-saga, there is two parts: independent saga runner process and scheduler (https://github.com/redux-saga/redux-saga/blob/master/src/internal/runSaga.js) , which is launched by sagaMiddleware.run() command, and effects reactions, which delegates actions into main saga process.

So, simplest process manager in ES6, which emulates redux-saga behavior, will be like that (very simplified):

const ProcessManager = (() => {
let context = new WeakMap();
function PM(rootSaga, lastValue) {
    if(!context.has(rootSaga)) {
        context.set(rootSaga, rootSaga())
    }
    const iterator = context.get(rootSaga);
    const { done, value } = iterator.next(lastValue);
    if(done) {
        context.delete(rootSaga)
        return;
    }
    if(Promise.resolve(value) === value) {
        value.then((asyncValue) => PM(rootSaga, asyncValue))
    } else {
        PM(rootSaga, value)
    }
}
return PM;
})()

const rootSaga = function* () {
    yield new Promise(resolve => setTimeout(resolve, 500));
    console.log('This will be printed after 500 ms from start');
    yield new Promise(resolve => setTimeout(resolve, 500));
    console.log('This will be printed after 1000 ms from start');
}

ProcessManager(rootSaga);