You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript单例模式报错:route_socket.getInstance不是函数

解决“Uncaught TypeError: route_socket.getInstance is not a function”问题

问题核心原因

你遇到的错误本质是AMD规范的require函数是异步执行的:它的返回值并不是你在回调函数中返回的那个包含getInstance方法的对象。当你在require外部直接调用route_socket.getInstance()时,依赖还未加载完成,回调函数也没执行,此时route_socket只是AMD加载器的内部处理对象,自然没有getInstance方法。

解决方案

方案1:将单例调用逻辑放入require回调内部

既然依赖加载是异步的,所有依赖RouteSocket的代码都应该放在require的回调函数里,确保依赖加载完成后再执行:

require([
    "esri/WebMap",
    "esri/views/MapView",
    "esri/layers/RouteLayer",
    "esri/rest/support/Stop"
  ],
  function(WebMap, MapView, RouteLayer, Stop) {
    var instance;

    class RouteSocket {
      #apiKey;

      constructor() {
        this.#apiKey = token.getAttribute("token");
        this.routeUrl = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World";
        this.churches = [];
        this.stompClient = null;
        this.view = null;
        this.connect();
        this.origin = new Stop({
          geometry: {
            x: OX,
            y: OY
          },
          name: "Origin"
        })
        this.destination = new Stop({
          geometry: {
            x: DX,
            y: DY
          },
          name: "Destination"
        })
        this.addRandomChurch();
        this.addRandomChurch();
        this.addRandomChurch();
      }

      addRandomChurch() {
        var randomX = ((OX - DX) * Math.random()) + OX;
        var randomY = ((OY - DY) * Math.random()) + OY;
        var ChurchStop = {
          x: randomX,
          y: randomY,
          name: "Church"
        };
        this.churchAdded(ChurchStop);
      }

      setView(view) {
        this.view = view;
        console.log("We have set the view");
        this.churches.forEach(church => this.addRoute(church));
      }

      connect() {
        var socket = new SockJS('/ws');
        this.stompClient = Stomp.over(socket);
        this.stompClient.connect({}, this.onConnected.bind(this), this.onError.bind(this));
      }

      async addRoute(church) {
        let stops = [
          this.origin,
          church,
          this.destination
        ];

        let routeLayer = new RouteLayer({
          stops
        });
        this.view.map.add(routeLayer);
        await routeLayer.load();
        const results = await routeLayer.solve({
          apiKey: this.#apiKey
        });
        routeLayer.update(results);
      }

      onConnected() {
        this.stompClient.subscribe('/topic/map/' + hobnobID.getAttribute("hobnobID"), this.onMessageReceived.bind(this));
        synchroniseDataMapSocket.establishSocket(this);
      }

      onMessageReceived(payload) {
        var message = JSON.parse(payload.body);

        var ChurchStop = {
          x: message.X,
          y: message.Y,
          name: "Church"
        };
        this.churchAdded(ChurchStop);
      }

      churchAdded(ChurchStop) {
        if (!this.view) {
          this.churches.push(ChurchStop);
          return;
        } else {
          this.addRoute(ChurchStop);
        }
      }
    };

    function createInstance() {
      var object = new RouteSocket();
      return object;
    }

    // 在这里获取并使用单例实例
    const routeSocketInstance = (function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    })();

    // 后续需要操作单例的代码都放在此处
    // 示例:routeSocketInstance.setView(yourMapView);
  });

方案2:用Promise封装AMD加载(多场景复用)

如果需要在多个地方复用这个单例,可以把AMD加载逻辑封装成Promise,通过异步方式获取实例:

// 封装单例获取逻辑为Promise
const getRouteSocketInstance = () => {
  return new Promise((resolve) => {
    require([
      "esri/WebMap",
      "esri/views/MapView",
      "esri/layers/RouteLayer",
      "esri/rest/support/Stop"
    ], function(WebMap, MapView, RouteLayer, Stop) {
      var instance;

      class RouteSocket {
        #apiKey;

        constructor() {
          this.#apiKey = token.getAttribute("token");
          this.routeUrl = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World";
          this.churches = [];
          this.stompClient = null;
          this.view = null;
          this.connect();
          this.origin = new Stop({
            geometry: {
              x: OX,
              y: OY
            },
            name: "Origin"
          })
          this.destination = new Stop({
            geometry: {
              x: DX,
              y: DY
            },
            name: "Destination"
          })
          this.addRandomChurch();
          this.addRandomChurch();
          this.addRandomChurch();
        }

        // 其余方法保持不变...
        addRandomChurch() {
          var randomX = ((OX - DX) * Math.random()) + OX;
          var randomY = ((OY - DY) * Math.random()) + OY;
          var ChurchStop = {
            x: randomX,
            y: randomY,
            name: "Church"
          };
          this.churchAdded(ChurchStop);
        }

        setView(view) {
          this.view = view;
          console.log("We have set the view");
          this.churches.forEach(church => this.addRoute(church));
        }

        connect() {
          var socket = new SockJS('/ws');
          this.stompClient = Stomp.over(socket);
          this.stompClient.connect({}, this.onConnected.bind(this), this.onError.bind(this));
        }

        async addRoute(church) {
          let stops = [
            this.origin,
            church,
            this.destination
          ];

          let routeLayer = new RouteLayer({
            stops
          });
          this.view.map.add(routeLayer);
          await routeLayer.load();
          const results = await routeLayer.solve({
            apiKey: this.#apiKey
          });
          routeLayer.update(results);
        }

        onConnected() {
          this.stompClient.subscribe('/topic/map/' + hobnobID.getAttribute("hobnobID"), this.onMessageReceived.bind(this));
          synchroniseDataMapSocket.establishSocket(this);
        }

        onMessageReceived(payload) {
          var message = JSON.parse(payload.body);

          var ChurchStop = {
            x: message.X,
            y: message.Y,
            name: "Church"
          };
          this.churchAdded(ChurchStop);
        }

        churchAdded(ChurchStop) {
          if (!this.view) {
            this.churches.push(ChurchStop);
            return;
          } else {
            this.addRoute(ChurchStop);
          }
        }
      };

      function createInstance() {
        var object = new RouteSocket();
        return object;
      }

      if (!instance) {
        instance = createInstance();
      }
      resolve(instance);
    });
  });
};

// 使用时通过Promise获取实例
getRouteSocketInstance().then(instance => {
  console.log("单例实例已获取:", instance);
  // 示例:instance.setView(yourMapView);
});

额外注意事项

  • 确保全局变量token、OX、OY、DX、DY、hobnobID、SockJS、Stomp、synchroniseDataMapSocket在代码执行时已正确定义,否则会引发新的错误。
  • 两种方案都保留了单例模式的核心逻辑:确保RouteSocket实例只被创建一次。

内容的提问来源于stack exchange,提问作者Drivers Sea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 18:55:25