如何从JavaScript中的递归生成器函数返回?

时间:2016-08-26 17:06:50

标签: javascript recursion ecmascript-6

我正在玩一个递归生成器函数,它以异步方式返回值。我正在使用协程包装函数来调用它。代码和JSBin如下:

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);

运行此命令返回:

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}

生成器的最终return怎么来undefined?当我将上述内容用于bluebird的Promise.coroutine时,我会得到相同的结果。我错过了关于递归生成器的基本内容吗?如何将其转到{ value: 5, done: true }

3 个答案:

答案 0 :(得分:4)

问题是您要返回count,但是您要在父生成器中返回它。与委派生成器中的yield不同,return不会通过委派链自动备份。

如果要获取委派生成器的return值,则必须直接在父生成器中分配它:

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

因为您正在使用&#34;递归&#34;生成器(多级授权),您需要重复该过程并在每个授权级别返回值:

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;
    }
}

答案 1 :(得分:2)

if中,您的一边只有return

您也不需要在发电机内使用.then。使用生成器的全部意义在于,您无需触及其中的promise 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,而我们正在使用它......

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

......最好...... ...

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

现在一起

运行代码段,您将在此处看到正确的输出

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 2 :(得分:1)

对于那些想知道的人:这不是coroutine助手的用法。函数本身应该通过包装版本递归,如下所示:

&#13;
&#13;
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);
&#13;
&#13;
&#13;

为什么?

异步函数定义为常规函数,返回Promise,永不抛出同步异常。 coroutine帮助程序只是帮助您编写异步函数。如果您熟悉其他语言的async / await,则此包装器旨在将生成器转换为异步函数,其中所有await都由yield替换。考虑到这一点,这些函数更容易推理,因此人们不必争论生成器,只需要异步函数。