首页 > 解决方案 > 即使我正在使用 Promises,我似乎也无法弄清楚为什么第三个函数在其他两个函数之前运行。我究竟做错了什么?

问题描述

当我 console.log this.state.events 和 this.state.meets 终端记录这两个状态是空数组。这表明该函数在函数 2 和 3 完成之前执行,并且我的承诺是错误的。我似乎无法弄清楚我的问题,我很困惑。我知道 setState 也可以正常工作,因为一旦页面呈现,我就有一个按钮,其功能 console.logs 这些状态并正确执行。

我尝试过以各种方式格式化我的承诺,但我不知道在我的函数完成之前它们是如何解决的。

obtainEventsAndMeetsRefs = () => {
  return Promise.resolve(
   this.userRef.get().then(
     doc => {
       this.setState(
         {
           eventRefs: doc.data().Events,
           meetingsRef: doc.data().Meets
         }
       )
     }
   )
 )
}

obtainEvents() {

var that = this

return new Promise(function (resolve, reject) {
  for (i = 0; i < that.state.eventRefs.length; i++) {

    docRef = that.state.eventRefs[i]._documentPath._parts[1];
    firebase.firestore().collection('All Events').doc(docRef).get().then(
      doc => {
        that.setState({
          events: update(that.state.events, {
            $push: [
              {
                eventName: doc.data().eventName,
                eventDescription: doc.data().eventDescription,
                startDate: doc.data().startDate,
                endDate: doc.data().endDate,
                location: doc.data().location,
                privacy: doc.data().privacy,
                status: doc.data().status,
                attending: doc.data().attending
              }
            ]
          })
        })
      }
    )

    if (i == that.state.eventRefs.length - 1) {
      console.log('1')
      resolve(true)
    }
  }
})
}

obtainMeetings() {

var that = this

return new Promise(function (resolve, reject) {

  for (i = 0; i < that.state.meetingsRef.length; i++) {

    userRef = that.state.meetingsRef[i]._documentPath._parts[1];
    meetRef = that.state.meetingsRef[i]._documentPath._parts[3];
    ref = firebase.firestore().collection('Users').doc(userRef)
      .collection('Meets').doc(meetRef)

    ref.get().then(
      doc => {

        that.setState({
          meets: update(that.state.meets, {
            $push: [
              {
                headline: doc.data().headline,
                agenda: doc.data().agenda,
                startTime: doc.data().startTime,
                endTime: doc.data().endTime,
                date: doc.data().date,
                name: doc.data().name
              }
            ]
          })
        })

      }
    )
    if (i == that.state.meetingsRef.length - 1) {
      console.log('2')
      resolve(true)
    }
  }
})
}

orderByDate = () => {
  console.log(this.state.events)
  console.log(this.state.meets)
}

componentDidMount() {
  this.obtainEventsAndMeetsRefs().then(
    () => this.obtainEvents().then(
      () => this.obtainMeetings().then(
        () => this.orderByDate()
      )
    )
  )
}

我希望输出是一个填充了我使用 setState 输入的数据的数组,而不是一个空数组。

标签: javascriptreactjspromise

解决方案


问题的症结在于,当异步操作完成时obtainMeetings()obtainEvents()不返回解决的承诺。因此,您无法知道他们的工作何时完成,因此您无法正确地对它们进行排序。

这段代码有各种各样的错误。首先,您不需要将现有的 Promise 包装在另一个 Promise 中(Promise 构造反模式),因为这会导致错误(您正在制作)。相反,您可以只返回现有的承诺。

例如,改变这个:

obtainEventsAndMeetsRefs = () => {
  return Promise.resolve(
   this.userRef.get().then(
     doc => {
       this.setState(
         {
           eventRefs: doc.data().Events,
           meetingsRef: doc.data().Meets
         }
       )
     }
   )
 )
}

对此:

obtainEventsAndMeetsRefs = () => {
   return this.userRef.get().then(doc => {
       this.setState({
           eventRefs: doc.data().Events,
           meetingsRef: doc.data().Meets
       });
   });
}

然后,链接而不是嵌套并返回与操作的完成/错误相关 的承诺。

所以改变这个:

componentDidMount() {
  this.obtainEventsAndMeetsRefs().then(
    () => this.obtainEvents().then(
      () => this.obtainMeetings().then(
        () => this.orderByDate()
      )
    )
  )
}

对此:

componentDidMount() {
  return this.obtainEventsAndMeetsRefs()
    .then(this.obtainEvents.bind(this))
    .then(this.obtainMeetings.bind(this))
    .then(this.orderByDate.bind(this))
}

或者,如果您愿意:

componentDidMount() {
  return this.obtainEventsAndMeetsRefs()
    .then(() => this.obtainEvents())
    .then(() => this.obtainMeetings())
    .then(() => this.orderByDate())
}

这些链而不是嵌套,它们返回一个跟踪整个链的承诺,以便调用者知道链何时完成和/或是否存在错误。

然后,如果你想让你的for循环串行执行,你可以像这样在循环内的 Promise 上async使用await它:

async obtainMeetings() {

    for (let i = 0; i < this.state.meetingsRef.length; i++) {

        let userRef = this.state.meetingsRef[i]._documentPath._parts[1];
        let meetRef = this.state.meetingsRef[i]._documentPath._parts[3];
        let ref = firebase.firestore().collection('Users').doc(userRef)
            .collection('Meets').doc(meetRef)

        // make the for loop wait for this operation to complete
        await ref.get().then(doc => {
            this.setState({
                meets: update(this.state.meets, {
                    $push: [{
                        headline: doc.data().headline,
                        agenda: doc.data().agenda,
                        startTime: doc.data().startTime,
                        endTime: doc.data().endTime,
                        date: doc.data().date,
                        name: doc.data().name
                    }]
                });
            });
        });
        // this return here isn't making a whole lot of sense
        // as your for loop already stops things when this index reaches
        // the end value
        if (i == this.state.meetingsRef.length - 1) {
            console.log('2')
            return true;
        }
    }
    // it seems like you probably want to have a return value here
    return true;
}

这看起来在您的实现中缺少i,userRef和的声明meetRef也存在问题。ref这些应该是本地声明的变量。

obtainEvents()需要同样的重新设计。

如果您不能使用async/await,那么您可以进行转换,或者您必须设计循环以不同方式工作(更多的是手动异步迭代)。


如果您可以并行运行循环中的所有异步操作,并且只想知道它们何时完成,您可以执行以下操作:

obtainMeetings() {

    return Promise.all(this.state.meetingsRef.map(item => {
        let userRef = item._documentPath._parts[1];
        let meetRef = item._documentPath._parts[3];
        let ref = firebase.firestore().collection('Users').doc(userRef)
            .collection('Meets').doc(meetRef)

        // make the for loop wait for this operation to complete
        return ref.get().then(doc => {
            this.setState({
                meets: update(this.state.meets, {
                    $push: [{
                        headline: doc.data().headline,
                        agenda: doc.data().agenda,
                        startTime: doc.data().startTime,
                        endTime: doc.data().endTime,
                        date: doc.data().date,
                        name: doc.data().name
                    }]
                });
            });
        });

    })).then(allResults => {
        // process all results, decide what the resolved value of the promise should be here
        return true;
    });
}

推荐阅读