Express 4 - 用 promise.then 链接 res.json 不起作用

Posted

技术标签:

【中文标题】Express 4 - 用 promise.then 链接 res.json 不起作用【英文标题】:Express 4 - chaining res.json with promise.then does not work 【发布时间】:2016-10-31 08:01:16 【问题描述】:

我正在开发一个使用 mysqlsequelize 包的 express 4 应用程序。 Sequelize ORM 使用 Promise 从数据库中获取数据。我正在尝试在路由器中获取数据并发送 json 响应。当我尝试用res.json 链接then 的承诺回调时,我在控制台中收到错误消息Unhandled rejection TypeError: Cannot read property 'get' of undefined

// This works
employeeRouter.get("/:id", function(req, res)
   Employee.findById(req.params.id).then(function(data)
      res.json(data);
   );
);

// Replacing above code with following doesn't work
employeeRouter.get("/:id", function(req, res)
   Employee.findById(req.params.id).then(res.json);
);

错误堆栈:

Unhandled rejection TypeError: Cannot read property 'get' of undefined
    at json (D:\Workstation\DataPro\CountryStats\node_modules\express\lib\response.js:241:21)
    at tryCatcher (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\util.js:16:23)
    at Promise._settlePromiseFromHandler (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\promise.js:504:31)
    at Promise._settlePromise (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\promise.js:561:18)
    at Promise._settlePromise0 (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\promise.js:606:10)
    at Promise._settlePromises (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\promise.js:685:18)
    at Async._drainQueue (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\async.js:138:16)
    at Async._drainQueues (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\async.js:148:10)
    at Immediate.Async.drainQueues [as _onImmediate] (D:\Workstation\DataPro\CountryStats\node_modules\bluebird\js\release\async.js:17:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

models/employee.js

var Sequelize = require('sequelize'),
    sequelize = require('../db-connect/sequelize');

(function()

  // Use Strict Linting
  'use strict';

  // Define Sequalize
  var Employee = sequelize.define('employee', 
    empNo:  field: 'emp_no', type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true ,
    birthDate:  field: 'birth_date', type: Sequelize.DATE ,
    firstName:  field: 'first_name', type: Sequelize.STRING ,
    lastName:  field: 'last_name', type: Sequelize.STRING ,
    gender:  field: 'gender', type: Sequelize.ENUM('M', 'F') ,
    hireDate:  field: 'hire_date', type: Sequelize.DATE ,
  );

  // Export
  module.exports = Employee;

());

db-connect/sequelize.js

var Sequelize = require('sequelize');

(function()

  // Use Strict Linting
  'use strict';

  // Sequalize Connection
  var sequelize = null;

  // Create Sequalize Connection
  if(!sequelize)
    sequelize = new Sequelize('employees', 'root', '', 
      host: 'localhost',
      dialect: 'mysql',
      define: 
        timestamps: false
      
    );
  

  module.exports = sequelize;

());

routes/employees.js

var express = require('express'),
    Employee = require('../models/employee');

(function(app)

  // Use Strict Linting
  'use strict';

  // Create Router
  var employeeRouter = express.Router();

  // Home Page
  employeeRouter.get("/", function(req, res)
    res.json(employees: ['all']);
  );

  // Get Specific Employee
  employeeRouter.get("/:id", function(req, res, next)
    Employee.findById(req.params.id).then(function(data)
      res.json(data);
    );
  );

  // ----------------------------------
  // Export
  // ----------------------------------

  module.exports = employeeRouter;

());

【问题讨论】:

【参考方案1】:

当您将res.json 作为函数传递时,res 对象会丢失,因此当json() 执行时,它没有对象并且您会看到您看到的错误。您可以使用.bind() 来解决这个问题:

employeeRouter.get("/:id", function(req, res)
   Employee.findById(req.params.id).then(res.json.bind(res));
);

这将确保res 对象在方法执行时与您的方法保持一致。如上使用.bind()本质上与:

employeeRouter.get("/:id", function(req, res)
   Employee.findById(req.params.id).then(function(data) 
       return res.json(data);
   );
);

事实上,.bind() 实际上创建了一个存根函数,就像上面示例中的匿名函数一样。它只是为你做,而不是让你去做。


再举个例子,假设您有两个单独的 res 对象,res1res2,来自两个单独的请求。

var x = res1.json;
var y = res2.json;

console.log(x === y);    // true, no association with either res1 or res2 any more

这是因为引用 res1.json 只会获得对 .json 方法的引用。它使用res1 来获取该方法(从 res1 原型中获取,但它具有该方法,它只是一个指向该方法的指针,不再与包含该方法的对象关联。所以,当你将res.json 传递给一个函数,你不会得到res 的附件。然后当你传递res.json 的函数去实际调用你的函数时,它会这样调用它:

var z = res.json;
z();

并且,当调用z() 时,json 内部的this 值最终变为undefined,并且与res 对象没有任何连接。使用 .bind() 创建一个存根函数,将其调用为 res.json(...) 以保持与对象的连接,并确保在执行该方法时正确设置 this

【讨论】:

只是为了确保我理解。 res 是未定义的,因为传递 res.json 而没有 () 什么是存根函数? @d9ngle - 存根函数只是一个简短的函数,它封装了另一个函数,并对封装函数的调用方式进行了轻微调整。您可以查看 .bind() here on MDN 的 polyfill 以查看其工作原理的示例。

以上是关于Express 4 - 用 promise.then 链接 res.json 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS -Express 4.0 用include取代partial

promise 理解

promise 理解

用正则表达式实现 运算 express = '1 -2* ((60-30 +(-40/5) *(9-2*5/3 +7 /3*99 /4*2998 +10 *568 /14))-

async & await

为啥在两次调用 promise 时 RSVP Deferred 会产生错误