How to return from a recursive generator function in JavaScript?

I play with a recursive generator function that returns values ​​asynchronously. I use the coroutine shell function to call it. Code and JSBin below:

http://jsbin.com/nuyovay/edit?js,console

let log = console.log.bind(console);
let err = console.error.bind(console);

function coroutine(generatorFn){
    return function co() {
        let generator = generatorFn.apply(this, arguments);

        function handle(result) {
            console.log(result);
            if (result.done) {
                return Promise.resolve(result.value);
            }
            return Promise.resolve(result.value)
                .then(
                    res => handle(generator.next(res)),
                    err => handle(generator.throw(err))
                );
        }

        try {
            return handle(generator.next());
        } catch (err) {
            return Promise.reject(err);
        }
    };
}

function sleep(dur) {
    return new Promise(res => {
        setTimeout(() => { res() }, dur);
    });
}

function* recurse(limit = 5, count = 0) {   
    if(count < limit) {
        yield sleep(100).then(() => Promise.resolve(++count));
        yield* recurse(limit, count);
    }
    else {
        return count;
    }
}

let test = coroutine(recurse);

test().then(log).catch(err);

Running this return:

Object {value: Promise, done: false}
Object {value: Promise, done: false}
Object {value: Promise, done: false}
Object {value: Promise, done: false}
Object {value: Promise, done: false}
// `value` should be 5
Object {value: undefined, done: true}

How is the final returnfrom the generator undefined? When I adapt the above for use with bluebird Promise.coroutine, I get the same result. Did I forget something about recursive generators? How do I get to { value: 5, done: true }?

+4
source share
3 answers

, count, . yield , return .

return , :

let returnValue = yield* recurse(limit, count);

"" ( ), :

function* recurse(limit = 5, count = 0) {   
    if(count < limit) {
        yield sleep(100).then(() => Promise.resolve(++count));
        let result = yield* recurse(limit, count); // save the return value
        return result; // return it to the parent
    }
    else {
        return count;
    }
}
+3

if return .

.then . , API .

recurse count + 1

function* recurse(limit = 5, count = 0) {
  if(count < limit) {
    yield sleep(1000).then(() => Promise.resolve(++count));
    return yield* recurse(limit, count + 1);
  }
  else {
    return count;
  }
}

ES6, & hellip;

return function co() {
    let generator = generatorFn.apply(this, arguments);

& hellip; , & hellip;

return function co(...args) {
    let generator = generatorFn(...args)

, .

let log = console.log.bind(console);
let err = console.error.bind(console);

function coroutine(generatorFn){
  return function co(...args) {
    let generator = generatorFn(...args)

    function handle(result) {
      console.log(result);
      if (result.done) {
        return Promise.resolve(result.value);
      }
      return Promise.resolve(result.value)
        .then(
          res => handle(generator.next(res)),
          err => handle(generator.throw(err))
        );
    }

    try {
      return handle(generator.next());
    } catch (err) {
      return Promise.reject(err);
    }
  };
}

function sleep(dur) {
  return new Promise(res => {
    setTimeout(() => { res() }, dur);
  });
}

function* recurse(limit = 5, count = 0) {  
  if(count < limit) {
    yield sleep(100)
    return yield* recurse(limit, count + 1);
  }
  else {
    return count;
  }
}

let test = coroutine(recurse);

test().then(log).catch(err);
Hide result
+2

, : , coroutine. wrapperd, :

let log = console.log.bind(console);
let err = console.error.bind(console);

function coroutine(generatorFn){
    return function co() {
        let generator = generatorFn.apply(this, arguments);

        function handle(result) {
            // console.log(result);
            if (result.done) {
                return Promise.resolve(result.value);
            }
            return Promise.resolve(result.value)
                .then(
                    res => handle(generator.next(res)),
                    err => handle(generator.throw(err))
                );
        }

        try {
            return handle(generator.next());
        } catch (err) {
            return Promise.reject(err);
        }
    };
}

function sleep(dur) {
    return new Promise(res => {
        setTimeout(() => { res() }, dur);
    });
}

const recurse = coroutine(function* (
  limit = 5, count = 0
) {   
  if(count < limit) {
    yield sleep(100);
    ++count;
    return yield recurse(limit, count);
  } else {
    return count;
  }
});


recurse().then(log).catch(err);
Hide result

?

An asynchronous function is defined as a regular function that returns Promiseand never throws a synchronous exception. The helper coroutinejust helps you write asynchronous functions. If you are familiar with async / await from other languages, this shell is designed to convert generators to asynchronous functions, where everything is awaitreplaced with yields. These functions are easier to argue with this in mind, so people do not need to argue about generators, asynchronous functions.

+1
source

Source: https://habr.com/ru/post/1652632/


All Articles