在调用`.then`后执行副作用是否需要承诺A +承诺?

时间:2016-08-02 21:34:10

标签: javascript promise

在你致电.then之前,我Knex API for building a schema实际创建一张桌子的方式让我感到兴奋。

例如,此代码实际上不会影响数据库:

knex.schema.createTable('users', table => {
  table.string('name')
})

但是这段代码会:

knex.schema.createTable('users', table => {
  table.string('name')
}).then(console.log.bind(console))

这种行为(在调用.then之前没有做任何事情):

a)Promises A+ spec

要求

b)Promises A+ spec

禁止

c)未指明

我阅读了规范,似乎行为未指定,但我不确定。这似乎太重要了,不能指明。

更新

请参阅@ Vohuman的回答:Knex架构构建器上的then方法首先执行副作用,然后返回一个promise。因此,即使我的问题的答案是(b),Knex也不会违反规范。虽然在这种情况下选择then作为方法名称是非常误导的。

3 个答案:

答案 0 :(得分:2)

这种行为是错误的;调用then()不应该超出promise本身的任何副作用(即执行回调)。

然而,该规范实际上并没有说明这一点。

答案 1 :(得分:2)

这并非完全“错误”,尽管并不常见。 Knex需要.then因为它必须能够告诉查询何时完成,而不是中间构建。

Example from the docs(评论补充):

knex.select('name').from('users')    
  .where('id', '>', 20)              // Can I issue the query yet?
  .andWhere('id', '<', 200)          // Okay I'll...oh, you have more conditions.
  .limit(10)                         // Hey, I'm here, still ready...
  .offset(x)                         // Just let me know when you want me to--
  .then(function(rows) {             // OH OKAY I'll issue it now.
    return _.pluck(rows, 'name');
  })

Google的API.js助手follows this pattern too,专门用于即时与批量查询:

  

当您创建一个旨在将其添加到批处理中的请求时,请不要在将请求添加到批处理之后调用其then方法。如果在添加请求之前调用then方法,则会立即发送请求,而不是作为批处理的一部分。

正如SLaks指出的那样,在文档中的任何地方都没有明确指定:为了使对象符合Promises/A+,它必须有一个名为then的方法。语义和返回值,没有任何内容指定then不能有其他行为。当然,这会禁止这些API库将then方法重命名为更合适的方法,例如thenIssueRequestthenGetResponse。 (您可以添加别名,但必须存在then。)

作为一名API设计师,唯一的选择是将承诺的创建与then的链接分开,但需要注意的是几乎每次调用then都会有额外的方法调用先于或包裹它。因为then是访问结果的唯一方法,所以我可以理解如何优化常见情况会导致删除额外的方法。

fluentApi.createRequest().parameterA(1).parameterB(2).issue().then(...);
// or
fluentApi.issue(fluentApi.createRequest().parameterA(1).parameterB(2)).then(...);

最后,请记住,您应该始终catch承诺,这会触发请求:

knex.schema.createTable('users', table => {
  table.string('name')
}).catch(console.log.bind(console));

...以及both arguments to then are optional,所以如果你坚持跳过错误处理,你甚至可以空then

knex.schema.createTable('users', table => {
  table.string('name')
}).then(null);


knex.schema.createTable('users', table => {
  table.string('name')
}).then();

答案 2 :(得分:1)

knex是一个查询构建器。 then只是一个辅助函数,它执行生成的[SQL]语句并在后台调用promise对象的then函数。如果用then函数替换.toString,您将获得生成的字符串。 then这里不是promise对象的方法。

来自knex源代码:

Target.prototype.then = function(/* onFulfilled, onRejected */) {
   const result = this.client.runner(this).run()
   return result.then.apply(result, arguments);
};