Promises是调用`.then`后执行副作用所需的A+Promises吗

Are Promises A+ promises required to execute side effects AFTER `.then` is called?

本文关键字:副作用 A+Promises 执行 调用 then Promises      更新时间:2023-09-26

我被用于构建模式的Knex API在调用.then之前实际上并没有创建表的问题所困扰。

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

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+规范要求

b) 被Promises A+规范禁止

c) 未指定

我阅读了规范,似乎行为没有具体说明,但我不确定。这一点似乎太重要了,无法具体说明。

更新

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

这并不完全是"错误的",尽管它并不常见。Knex需要.then,因为它必须能够判断查询何时完成,而不是中期构建。

文档示例(添加注释):

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

谷歌的API.js助手也遵循这种模式,特别是针对即时查询和批量查询:

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

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

作为一名API设计师,唯一的选择是将promise的创建与then的链接分开,但需要注意的是,几乎每个对then的调用都会有一个额外的方法调用在其之前或包装它。因为then是访问结果的唯一方式,所以我可以理解为常见情况进行优化会如何导致删除额外的方法。

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

最后,请记住,您应该始终在某个时刻catch a Promise,这将触发请求:

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

并且then的两个参数都是可选的,所以如果你坚持跳过错误处理,你甚至可以有一个空的then:

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

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

此行为是错误的;调用then()除了promise本身(即执行回调)之外不应该有任何副作用。

然而,规范实际上并没有对此做任何说明。

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

来自knex源代码:

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