如何使用promises转换此示例代码到生成器?

时间:2017-07-18 13:10:37

标签: node.js asynchronous generator es6-promise

我是node.js的新手,我正在使用Node v6.10。我最近开始的项目中的大部分代码都是使用promises编写的,并使用MongoDB查询mongoose ODM,这是一个基于promise的解决方案。现在,我一直在阅读异步编程,发现生成器提供了比Promises或回调更清晰的代码,但我一直无法将代码移植到Generators。

所以我想知道是否有人可以指导我怎么做?

以下是我要转换的示例代码:

const Promise = require('bluebird');

class GeneratorTest {
    testFn(number) {
        if(number % 2 === 0) {
            return Promise.resolve();
        }
        return Promise.reject(new Error("Not Divisible by 2"));
    }
}

let obj = new GeneratorTest();

obj.testFn(Math.round(Math.random()*100))
.then(() => Math.round(Math.random()*100))
.then((res) => {
    if(res % 3 === 0) {
        return res;
    } else {
        return Promise.reject(new Error("Not Divisible by 3"));
    }
}).then((result) => {
    return "YES";
}).catch((reason) => {
    console.log(reason.message);
    return "NO";
});

谢谢!

编辑1:转换尝试使用co

const Promise = require('bluebird');
const co = require('co');

class GeneratorTest {
    testFn(number) {
        if(number % 2 === 0) {
            return Promise.resolve();
        }
        return Promise.reject(new Error("Not Divisible by 2"));
    }
}

let obj = new GeneratorTest();

let result = obj.testFn(Math.random()*100);



co(function* (){try {
        let res = yield obj.testFn(Math.round(Math.random()*100))
        if(res % 3 === 0) {
            return res;
        } else {
            throw new Error("Not Divisible by 3");
        }
    } catch(e) {
        console.log("IN CATCH");
        console.log(e)
    }});

但我收到错误:

Unhandled rejection Error: Not Divisible by 2
    at GeneratorTest.testFn (/home/ayush/Desktop/generator-test/index.js:9:31)
    at Object.<anonymous> (/home/ayush/Desktop/generator-test/index.js:15:18)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Function.Module.runMain (module.js:605:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:575:3

1 个答案:

答案 0 :(得分:0)

更新

问题已更新,所以这里有一个快速更新。 未处理的拒绝是由这一行引起的:

let result = obj.testFn(Math.random()*100);

你明显没有处理拒绝(并且你不太可能得到一个整数和偶数的数字)所以错误并不奇怪。

您可以更改:

console.log(e)

为:

console.log('Error:', e.message || e);

更简洁的输出,以便更清楚地了解更多信息。

原始答案

首先,与if(number % 1 === 0)消息相反的代码"Not Divisible by 2"测试数字是否可被1整除(不是2),并且对于任何整数都是如此。这意味着您在问题中包含的代码中无法获得Not Divisible by 2承诺拒绝(因为您在代码中使用Math.round)并且您包含的错误必须具有由一些其他代码打印,而不是您在此处提供的代码。

这个特定的代码很难转换,同时显示了差异(因为你忽略了返回值),但我会给你一些一般的例子来说明返回值和异常的位置来自他们最终的地方,以及它们与承诺解决或拒绝价值的关系。

如果您有一个返回承诺的函数,那么通常将其运行为:

f()
  .then((value) => {
    console.log('Value:', value);
  })
  .catch((error) => {
    console.log('Error:', error);
  });

f()函数返回一个可以拒绝或解决的承诺,并打印正确的消息。

使用基于生成器的协同程序(如co或Bluebird.coroutine),它将是:

try {
  let value = yield f();
  console.log('Value:', value);
} catch (error) {
  console.log('Error:', error);
}

此处f()函数仍会返回承诺 - 它可能与前一个示例中的f()相同。

使用ES8的async / await语法,它将是:

try {
  let value = await f();
  console.log('Value:', value);
} catch (error) {
  console.log('Error:', error);
}

请记住,使用yield的代码必须包装在正确的协同程序包装中,如下所示:

co(function* () {
  // ...
});

或:

co.wrap(function* () {
  // ...
})();

或:

P.coroutine(function* () {
  // ...
});

(其中P是Bluebird的承诺),使用await的代码必须位于使用async关键字声明的函数中:

(async () {
  // ...
})();

重要的是,所有async函数都返回一个promise,就像协程包装器返回的所有函数一样(如co.wrapP.coroutine)。由于此承诺拒绝和任何返回的值都会解除承诺,因此正文中抛出的任何异常都会结束。