在这种情况下我必须同步 RPC 吗?

Do I have to synchronize the RPCs in this scenario?

昨天我意识到,如果我不同步我的异步请求,我正在做的事情可能会成为一个问题 - 问题是我是否 真的 必须这样做。

让我们考虑以下场景:我有一个顶级视图,它将数据列表 List<SomeObject> 向下传递到它的子视图。所有子级演示者都将通过添加、删除和修改该列表的项目来处理该数据列表。

假设用户在 UI 中做了一些事情,然后点击多个子级视图实例上的 "Save" 按钮,这将调用 SubLevelPresenter.onSaveSomeObjectButtonClick()。这将执行一个 RPC 调用,结果将被添加到 List<Somebject> 列表中。

我的假设是,如果用户单击两个不同的保存按钮,数据可能会同时到达,同时触发 onSaveSomeObjectButtonClick(),这将导致同时访问 List<SomeObject>

我是否必须通过实施队列来同步这些操作,例如还是由于我在这里看不到的一些内部 GWT/JavaScript 魔法,我安全吗?

public class TopLevelPresenter {

    private List<SomeObject> someObjectList = new ArrayList<>();    
    private List<SubLevelPresenter> cache = new ArrayList<>();

    public void TopLevelPresenter(TopLevelModel topLevelModel, TopLevelView topLevelView) {

        this.topLevelModel = topLevelModel;
        this.topLevelView = topLevelView;

        for(int i = 0; i < topLevelModel.getNumOfSubViews(); i++) {

            // Pass the data
            SubLevelModel subLevelModel = new SubLevelModel(someObjectList);
            SubLevelView subLevelView = new SubLevelView();         
            SubLevelPresenter subLevelPresenter = new SubLevelPresenter(subLevelModel, subLevelView);
            cache.add(subLevelPresenter);
        }
    }
}

public class SubLevelModel() {

    private List<SomeObject> someObjectList;

    public SubLevelModel(List<SomeObject> someObjectList) {
        this.someObjectList = someObjectList;
    }

    public void addSomeObject(SomeObject someObject) {
        this.someObjectList.add(someObject);
    }

    public void removeSomeObject(SomeObject someObject) {
        this.someObjectList.remove(someObject);
    }
}

public class SubLevelPresenter() {

    private SomeServiceAsync someService = /* .. */;
    private SubLevelView subLevelView;  
    private SubLevelModel subLevelModel;

    public SubLevelPresenter(SubLevelModel subLevelModel, SubLevelView subLevelView) {
        this.subLevelView = subLevelView;
        this.subLevelModel = subLevelModel;
    }

    public void onSaveSomeObjectButtonClick() {

        SomeObject toSave = this.subLevelView.getSuggestionBox().getSelection();

        someService.saveSomeObject(toSave, new AsyncCallback<SomeObject>() {

            @Override
            public void onFailure(Throwable caught) {
                Window.alert("RPC to saveSomeObject() failed.");
            }

            @Override
            public void onSuccess(SomeObject savedObject) {
                SubLevelPresenter.this.subLevelModel.addSomeObject(savedObject);
            }
        });
    }
}

对于您的方案,在 RPC 之后让多个实例将数据放入您的列表中似乎没问题。但是,在这里使用同步列表对同时访问进行排队可能是一个很好的解决方案。

Collections.synchronizedList(new ArrayList<String>());

鉴于 JavaScript 是单线程的,synchronized 关键字在客户端代码中无用(并被 GWT 忽略)。即使 RPC 响应同时到达,它们也会在 JavaScript 事件循环中排队并一个接一个地处理。

请注意浏览器中存在错误(即 Firefox) leading to race conditions, but they cannot be detected and/or worked-around as easily as dropping some synchronized (or equivalent) keyword in the code. Also note the Firefox bugalert()confirm() 有关。