javascript - 由于竞争条件,MobX Action 计划/执行顺序未保留

标签 javascript ecmascript-6 mobx

这是我到目前为止所得到的一个运行示例:

https://codesandbox.io/s/github/BruceL33t/mobx-action-synchronous-execution-order/tree/master/

store.js:

import { observable, action } from "mobx";
import Sensor from "../models/Sensor";

export default class RootStore {
  @observable sensors = new Map();

  constructor() {
    let self = this;
    const sensorIds = [
      "sensor1",
      "sensor2",
      "sensor3",
      "sensor4",
      "sensor5",
      "sensor6",
      "sensor7",
      "sensor8",
      "sensor9",
      "sensor10"
    ];

    for (let sensor of sensorIds) {
      self.sensors.set(sensor, new Sensor(5));
    }

    // setInterval simulates some incoming data (originally from SignalR, and roughly each second)
    setInterval(function() {
      let out = {};
      const x = +new Date(); // unix timestamp
      for (let sensor of sensorIds) {
        const y = Math.floor(Math.random() * 10000) + 1;
        const m = { x: x, y: y };
        out[sensor] = m;
      }

      self.addMeasurement(out); // the problem starts here.
    }, 1000);
  }

  // the problem!
  @action
  addMeasurement(sensorMeasurementMap) {
    let self = this;
    // this timeout is to try and simulate a race condition
    // since each measurement is incoming each second,
    // here some of them will take as long as 6 seconds to add,
    // due to the timeout.
    // the point is that they should always be added,
    // in the order they were called in.
    // so if the first measurement takes 20 seconds to be added,
    // the next measurements that were received on 2, 3, 4, 5..., 19th second etc,
    // should all "wait" for the prev measurement, so they're added
    // in the right order (order can be checked by timestamp, x)
    setTimeout(() => {
      const keys = self.sensors.keys();

      if (keys.length === 0) {
        // never really gonna happen, since we already set them above
      } else {
        for (const key in sensorMeasurementMap) {
          if (self.sensors.keys().indexOf(key) > -1) {
            self.sensors.get(key).add(sensorMeasurementMap[key]);
          } else {
            // also not gonna happen in this example
          }
        }
      }
    }, Math.floor(Math.random() * 20 + 1) * 1000);
  }
}

传感器.js:

import Queue from './Queue';
import {observable, action} from 'mobx';

export default class Sensor {
  @observable queue;

  constructor(n) {
    this.n = n;
    this.queue = new Queue(this.n);
  }
  @action add(measurement) {
    this.queue.add(measurement);
  }
}

Queue.js:

import {observable, action} from 'mobx';

export default class Queue {
  @observable data;
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.size = 0;
    this.data = [];
  }

  @action add(measurement) {
    let removedItem = undefined;
    if(this.size >= this.maxSize) {
      let temp = this.data[0];
      removedItem = temp && temp.y ? temp.y+'' : undefined;
      this.data.shift();
    }

    this.data.push(measurement);

    if (removedItem === undefined && this.size < this.maxSize) {
      this.size++;
    }

    return removedItem;

  }
}

代码中有一些注释,但您绝对需要查看输出 https://codesandbox.io/s/github/BruceL33t/mobx-action-synchronous-execution-order/tree/master/了解它。

我也试着在这里解释一下,这是怎么回事。

这基本上是真实应用程序的一部分的过度简化版本,其中 setInterval 只是用来模拟 SignalR 事件处理程序以指示每秒传入的数据。传入数据是我们在 addMeasurement 操作上方的 setInterval 函数中创建的。

鉴于每秒接收到一些传入数据,我们希望将其添加到商店中的可观察 map 传感器中。由于此数据用于在实际应用程序中绘制图表,因此我们需要确保它确实按照调用操作的顺序添加 - 无论操作需要多长时间才能完成。

在实际应用中,我看到数据被推送到 MobX 状态的顺序有些不一致,所以我将其隔离并将相关部分提取到这个示例中,并试图通过使用里面的 setTimeout 函数来夸大它一点添加测量操作。

由于每个数据都是每秒获取的,但有些测量可能需要长达 20 秒的时间才能获取(这不现实,但为了清楚地显示竞争条件问题),就像现在的代码一样,我们经常会遇到这样的情况像这样的东西:

[
    {"x":1519637083193,"y":4411},
    {"x":1519637080192,"y":7562},
    {"x":1519637084193,"y":1269},
    {"x":1519637085192,"y":8916},
    {"x":1519637081192,"y":7365}
]

这真的不应该发生,因为 1519637083193 大于/晚于 1519637080192

当根据这些数据绘制图表并在之后对其进行排序时,这是一个真正的问题太昂贵了,所以我正在寻找一种方法来改进这段代码,这样我们就可以相信每个 addMeasurement 只有在前一个 Action 完全完成后才会被触发完成的。或者至少是一种以正确顺序更新 MobX 状态的方法

希望它有意义。

最佳答案

should all "wait" for the prev measurement, so they're added in the right order (order can be checked by timestamp, x).

您能详细说明一下吗?人们怎么知道将来不会收到比当前时间戳更大的时间戳,因此会无限期地等待?您正在寻找的不只是对测量值数组的排序插入(而不是等待)吗?

如果排序插入不能解决问题,我可能会执行以下操作(未经测试):

lastAddition = Promise.resolve() // start with already finishied addition

addMeasurement(sensorMeasurementMap) {
      this.lastAddition = this.lastAddition.then(() => {
          return new Promise((resolve, reject) => {
            setTimeout(action(() => {
                const keys = self.sensors.keys();

                if (keys.length === 0) {
                    // never really gonna happen, since we already set them above
                } else {
                    for (const key in sensorMeasurementMap) {
                    if (self.sensors.keys().indexOf(key) > -1) {
                        self.sensors.get(key).add(sensorMeasurementMap[key]);
                    } else {
                        // also not gonna happen in this example
                    }
                    }
                }
                resolve()
            }), Math.floor(Math.random() * 20 + 1) * 1000);
        })
      })
  }
}

注意:请注意,我将 action 移到了里面,因为您需要在实际修改状态的地方而不是调度发生的地方

关于javascript - 由于竞争条件,MobX Action 计划/执行顺序未保留,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48990760/

相关文章:

javascript - Jest - 函数不调用其子异步函数

javascript - Mobx 监听一个值的变化与计算不起作用

javascript - 为什么 JQuery 不显示 div

javascript - Bootstrap 主题 js 在生产中不起作用,但在开发中起作用(Rails)

javascript - 打开 Bootstrap 和 Angularjs 不工作的盒子

javascript - react ref 和 queryselectorall

javascript - 如何在 Vercel 上的 SvelteKit 应用程序中创建 Web Worker?

javascript - 在 ES6 中创建带箭头或不带箭头的顶级函数的优缺点是什么?

javascript - 我如何以及为什么要编写一个扩展 null 的类?

react-native - JS 线程似乎暂停了