我应该在每个类方法中创建一个新的 Promise 吗?

Posted

技术标签:

【中文标题】我应该在每个类方法中创建一个新的 Promise 吗?【英文标题】:Should I create a new Promise in each class method? 【发布时间】:2019-06-30 11:05:51 【问题描述】:

我想在我的类方法中使用 Promises。在Promise antipatterns 中,我读到为每个新功能创建一个新的承诺被认为是不好的。

但是,我不想在我的项目中返回不相关的承诺,所以我想做这样的事情:

class MyClass 

  async getToken() 
    return new Promise(
      (resolve, reject) => 
        // . . .
        const token_str = '<response_from_http_request>';
        resolve(token_str);
      
    )
  

  async doSomething(token) 
    return new Promise(
      (resolve, reject) => 
        const result = // . . .
        resolve(result);
      
    )
  

  async doAnotherSomething(token) 
    return new Promise(
      (resolve, reject) => 
        const result = // . . .
        resolve(result);
      
    )
  


然后我会这样使用它:

let instance = new MyClass();

(async () => 
    const token = await instance.getToken();

    const result1 = await instance.doSomething(token);
    console.log(result1);

    const result2 = await instance.doAnotherSomething(token);
    console.log(result2);

)();

这似乎是一种有效的方法,或者这也是一种反模式?如果是这样,我怎样才能避免编写这样的代码?


编辑:如果我需要进行多次连续的 http 调用,对结果执行一些操作,然后基于它返回一个 Promise?

按照我的理解,如果我不做出新的承诺,我必须返回got.js库做出的承诺,其中包括http响应数据。

相反,我想返回一个包含我的类方法结果的 Promise。

例子:
  async getCityWeather( city_name ) 
    return new Promise(
      (resolve, reject) => 

        // get the city id based on its name
        const city_id = await got(`https://my-api/getCityIdByName/$city_name`);

        // now get the weather info for the city with id `cityid`
        const weather_info = await got(`https://my-api/weatherById/$city_id`);

        // make an object to return
        const temperature = 
          weather_info.temp_min,
          weather_info.temp_max,
        

        resolve(temperature);

        // ... all error handling are omitted

      
    )
  

我不想返回包含 got.js 返回值的 Promise,我想根据 http 请求调用返回 my 值。

【问题讨论】:

// . . . 到底是什么?除非你在那里做任何异步操作,否则你根本不应该使用 Promise。 旁注:您的result1result2 仅依赖于令牌。然后你可以这样做:const [result1, result2] = await Promise.all([instance.doSomething(token), instance.doAnotherSomething(token)]); 不会导致阻塞。 @k0pernikus 两种方式都不会导致阻塞,因为它会阻塞线程。他们是浪费的,因为一个人需要等待另一个人。 @Bergi 我忘了说,在所有// . . . 标记的行中,我正在做异步got.js 调用。 @k0pernikus 很好的观察!但是,如果另一个变量失败,这不是无法填充result* 变量吗?至少这是我从Promise.all() 了解到的。无论如何,正如@Nik 所说,在调用独立的情况下使用await 是一种浪费。 【参考方案1】:

async 函数总是返回Promise

一个函数/方法在以下情况下会返回一个Promise:

您显式创建并从其主体返回了一个 Promise。 您返回了一个存在于方法之外的 Promise。 您将其标记为async

由于您可以 await 一个 Promise 并且 instance.doSomething 已经是一个异步标记的方法,您可以等待它而无需显式返回一个 Promise。

只需return,它的结果就像您在常规同步方法中一样。

我不想在我的项目中返回不相关的承诺...

除非您实际上在方法中执行异步操作(访问文件系统、数据库调用、计时器等...),否则您不需要将其包装在 Promise 中, 也不是 await 当您需要结果时。

您实际上需要在Promise 中包装某些内容的最常见情况是,如果您有一个使用callbacks 工作的异步函数,但您想将其用作Promise

// plain old callback-style asynchronous functions:
const getFooViaCallback = callback => 
  setTimeout(() => 
    callback('foo')
  , 150)


const getBarViaCallback = callback => 
  setTimeout(() => 
    callback('bar')
  , 150)


class Foo 
  constructor() 
  
  getFooViaPromise() 
    // wrap callback-style code in a Promise
    // so we can await it.
    return new Promise(resolve => 
      getFooViaCallback(result => 
        resolve(result)
      )
    )
  

  getBarViaPromise() 
    // wrap callback-style code in a Promise
    // so we can await it.
    return new Promise(resolve => 
      getBarViaCallback(result => 
        resolve(result)
      )
    )
  
  
  getBaz() 
    // no reason to wrap this in a Promise,
    // since it's a synchronous method.
    return 'baz'
  
  
  async getFooBarBaz() 
    const foo = await this.getFooViaPromise()
    const bar = await this.getBarViaPromise()
    const baz = this.getBaz()

    return foo + ',' + bar + ',' + baz
  


;(async() => 
  const foo = new Foo()
  
  const result = await foo.getFooBarBaz()
  console.log('foo.getFooBarBaz() result: ', result)
)()

为简洁起见,我在上面的 sn-p 中省略了错误处理,但您应该在 async 标记的方法中使用 throw 来引发错误。这相当于在 Promise 中调用 .reject()

【讨论】:

1.你能解释一下你的第一点吗?如何在我的代码中不使用new Promise() 的情况下使用resolve()reject() 反之亦然,不是吗?如果你要返回一个 Promise,你也不需要使方法异步。 @ConfusedGuy 完全正确,async 函数必须使用throw 才能引发错误。 @ConfusedGuy 不,您不需要“异步上下文”来使 got.js 工作。它返回承诺,仅此而已。当您想要await 这样的承诺时,您只需要一个async 函数上下文。 @Andy 它是双向的。如果你显式返回一个promise,你不需要创建函数async,但是相反,如果你创建函数async你不必返回一个promise,因为async函数隐式返回一个promise无论你返回什么都可以解决(或者你扔什么都拒绝)

以上是关于我应该在每个类方法中创建一个新的 Promise 吗?的主要内容,如果未能解决你的问题,请参考以下文章

何时在匿名类中创建方法

在系统中创建用户ID的最佳做法是什么?

在类中创建对象的最佳实践

会话丢失并在每个 servlet 请求中创建为新的

如何在已配置的SAP系统中创建一个新的公司(仅管理总账)

我应该在每个服务 spring kafka 中创建 NewTopics 吗?