Было получено значение, которое не может рассматриваться как обещание

Я использую Koa и Bluebird и пробую сопрограммы. Я считаю, что ниже дает мне ошибку. Но если я заменю yield initRouters() с содержанием initRouters() оно работает. Это почему?

'use strict';

const fs = require('fs');
const Promise = require('bluebird');
const readdir = Promise.promisify(fs.readdir);

let app = require('koa')();

let initRouters = function *() {
  const routerDir = `${__dirname}/routers`;
  let routerFiles = yield readdir(routerDir);
  return Promise.map(routerFiles, function *(file) {
    var router = require(`${routerDir}/${file}`);

    app
      .use(router.routes())
      .use(router.allowedMethods());
  });
}

Promise.coroutine(function *() {
  yield initRouters();

  const PORT = process.env.PORT || 8000;
  app.listen(PORT);
  console.log(`Server listening on ${PORT}`);
})();

Я получаю ошибку

Unhandled rejection TypeError: A value [object Generator] was yielded that could not be treated as a promise

    See http://goo.gl/MqrFmX

From coroutine:
    at Function.Promise.coroutine (/home/jiewmeng/Dropbox/expenses-app/node_modules/bluebird/js/release/generators.js:176:17)
    at Object.<anonymous> (/home/jiewmeng/Dropbox/expenses-app/src/app.js:21:9)
    at PromiseSpawn._continue (/home/jiewmeng/Dropbox/expenses-app/node_modules/bluebird/js/release/generators.js:145:21)
    at PromiseSpawn._promiseFulfilled (/home/jiewmeng/Dropbox/expenses-app/node_modules/bluebird/js/release/generators.js:92:10)
    at /home/jiewmeng/Dropbox/expenses-app/node_modules/bluebird/js/release/generators.js:183:15
    at Object.<anonymous> (/home/jiewmeng/Dropbox/expenses-app/src/app.js:27:3)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Function.Module.runMain (module.js:447:10)
    at startup (node.js:141:18)
    at node.js:933:3

2 ответа

Решение

Вы достигли проблемы с яйцом / курицей.

Promise.coroutine давайте дадим обещание:

Возвращает функцию, которая может использовать yield для получения обещаний.

Проблема здесь в том, что вы пытаетесь получить функцию генератора, а не обещание. Это приводит к ошибке:

Unhandled rejection TypeError: A value [object Generator] was yielded that could not be treated as a promise

Зачем?

Ну, функция генератора возвращает обещание, но это только обещание, когда вы его дадите. И поскольку вы не можете выдать функцию генератора, вы никогда не получите значение (обещание).

Вы можете (и должны) использовать coroutine а также yield initRouters();, НО это initRouters() должна быть нормальной функцией, которая возвращает обещание, а не функцией генератора.

Как примечание стороны, вам не нужно Promise.map() потому что все, что вы делаете внутри .map синхронно, так что вы можете прекрасно сделать:

let initRouters = function() {
  const routerDir = `${__dirname}/routers`;

  return readdir(routerDir).then(function (files) {
    files.map(function (file) {
      var router = require(`${routerDir}/${file}`);

      app
        .use(router.routes())
        .use(router.allowedMethods());
    })
  })
}

Здесь вы возвращаете обещание, которое разрешается, когда вы его даете. Но, как видите, вы уже возвращаете обещание, поэтому Promise.coroutine не дает вам никакой ошибки. В случае генератора вы не возвращаете обещание.

initRouters() возвращает генератор. Ты не можешь yield это к Promise.coroutine, что позволяет только давать обещания (как ясно указывает на ошибку).

Вам либо нужно обернуть initRouters в Promise.coroutine сам или использовать

yield* initRouters();

так что все обещания от генератора даются, а не сам генератор. Обратите внимание, что co библиотека допускает такие причуды, в отличие от Bluebird.

Другие вопросы по тегам