【问题标题】:Promise design pattern承诺设计模式
【发布时间】:2018-03-01 20:53:56
【问题描述】:

我创建了一个SoundManager 来处理我的应用程序中的声音。 在我的应用程序中,我实例化了一个 SoundManager,然后使用它来加载音频文件,并在加载后返回一个 Promise。

我已经省略了一些我认为在这种情况下不重要的代码 - 我知道它们都正确加载,例如,我的应用程序中的 then() 函数,它从 Promise 数组传递loadSounds() 接收一个包含所有声音(不是承诺)的对象。

但是,如果从我的应用程序(在所有声音都已加载的位置)调用playSound()this.sounds 变量将是一个 Promises 数组。

为什么会这样?承诺一旦兑现,SoundManager 中的变量this.sounds 不应该更新吗?

这看起来也不是一个好的模式,那么关于如何实现它有什么想法吗?

import Promise from 'bluebird';
import {Howl, Howler} from 'howler';

export default class SoundManager {
  constructor(){
    this.sounds;
  }

  loadSounds(soundsArray){
    this.sounds = soundsArray.map((data) => {
      return new Promise((resolve, reject) => {        
        // ...
      })
    })
    return Promise.all(this.sounds)
  }

  playSound(id){
    console.log('this.sounds: ', this.sounds); //this is an array of promises, when I expect an array of sound objects
    let sound = this.sounds.filter((sound) => sound.id === id);
    if (!sound.length) {
      console.warn(`could not find sound with id "${id}"`)
      return false;
    }
    sound[0].play()
  }
}

【问题讨论】:

  • 因为...您将该属性的值设置为一组承诺。混乱在哪里?该属性改为包含值数组是没有意义的,这些值当时不存在。
  • 是的,这很有意义,我理解。我实际上不需要向应用程序(调用 SoundManager.loadSounds())返回任何内容,但我希望它加载所有文件,将它们存储在一个变量中,然后返回到我的应用程序,所以它继续执行它东西
  • 这是一个愚蠢的问题吗?对不起,我病得很重,很累
  • 有点傻。您不能将 this.sounds 的值设置为一系列尚不存在的事物。这就是为什么它是一系列承诺。在 promise 解决后,您可以稍后将其重新定义为声音数组,但您必须确保稍后再访问它。
  • 有点冗长,但你应该在这里使用两个数组,一个 promise 数组,然后是一个声音数组,当上述 promise 实现时会填充它们

标签: javascript ecmascript-6 promise es6-promise


【解决方案1】:

this.sounds 包含一组承诺,因为这正是您在此处分配给它的内容:

  loadSounds(soundsArray){
    this.sounds = soundsArray.map((data) => {
      return new Promise((resolve, reject) => {        
        // ...
      })
    })
    return Promise.all(this.sounds)
  }

soundsArray.map() 的结果是一系列承诺。

如果您希望它包含最终值,您可以这样做:

  loadSounds(soundsArray){
    let sounds = soundsArray.map((data) => {
      return new Promise((resolve, reject) => {        
        // ...
      })
    })
    return Promise.all(sounds).then(data => {
        this.sounds = data;
        return data;
    });
  }

虽然这样做似乎有点问题,因为除了使用loadSounds() 返回的承诺之外,没有其他方法可以知道this.sounds 何时包含您想要的数据。因此,如果知道数据何时存在的唯一方法是loadSounds() 返回的承诺,那么尚不清楚其他代码如何能够可靠地使用this.sounds

您可以将来自Promise.all() 的承诺存储在this.sounds 中,然后任何需要声音的代码都可以使用this.sounds.then() 来获取它。我们必须更多地了解您正在尝试使用 this.sounds 做什么,以及您何时尝试这样做才能知道最好的设计是什么。

【讨论】:

    【解决方案2】:

    承诺一旦兑现,SoundManager 中的变量this.sounds 不应该更新吗?

    不,这不是承诺的工作方式。他们保持承诺对象,他们不会“成为”他们被实现的价值。

    这看起来不是一个好的模式

    是的,不要在管理器中加载声音。管理器在其生命周期内可能可用也可能不可用——这并不是特别有用。最好仅在加载声音后实例化它 - 请参阅this also here

    export default class SoundManager {
      constructor(sounds) {
        this.sounds = sounds;
      }
    
      static loadAndCreate(soundsArray) {
        return Promise.all(soundsArray.map((data) => {
          return new Promise((resolve, reject) => {        
            …
          })
        })).then(sounds => new this(sounds));
      }
    
      playSound(id){
        console.log('this.sounds: ', this.sounds); //this is always an array of sound objects
        let sound = this.sounds.find(sound => sound.id === id);
        if (!sound) {
          console.warn(`could not find sound with id "${id}"`)
          return false;
        }
        sound.play()
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-11-07
      • 2015-11-07
      • 1970-01-01
      • 2011-01-19
      • 2013-02-21
      • 1970-01-01
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多