【问题标题】:Using a Promise to create "atomic" blocks of code in Javascript使用 Promise 在 Javascript 中创建“原子”代码块
【发布时间】:2016-05-05 01:36:56
【问题描述】:

来自 Java 背景的我现在正试图围绕 Javascript 的异步特性来思考。我在我的代码中使用 Promise 来做到这一点,直到现在一切都像一个魅力,但现在我有一个概念性问题,即使在多次阅读 Promise/A+ 规范后也没有找到明确的答案。

我的要求是:我有一个修改共享对象的方法,将更新存储在 PouchDB 中,然后再将其读回,以便从数据库中获取更新的修订 id 字段(乐观锁定)。在 Pouch 中存储和更新数据是异步的(为简洁起见,我省略了存储“this”以从 Promise 中调用方法):

var _doc = ...;
var _pouch = new PouchDB(...);

function setValue(key, value) {
    _doc[key] = value;
    _pouch.put(_doc)
    .then(function() {
        return _pouch.get(_doc._id);
    })
    .then(function(updatedDoc) {
        _doc = updatedDoc;
    });
}

现在,我想确保在 _doc 被再次读取之前,没有在 _doc 上设置其他键。 (a)是否有可能另一个 setValue() 调用正在执行 put() (具有过时的修订 ID)而来自 Pouch 的 get() 调用尚未执行(鉴于 JS 正在使用的消息队列方法) 和 (b) 如果可能的话,以下解决方案是否安全(它在我的测试中有效,但由于我不知道我的测试是否考虑了所有可能性......;再次省略存储“this” ):

var _doc = ...;
var _pouch = new PouchDB(...);
var _updatePromise;

function setValue(key, value) {
    if (_updatePromise == null) {
        setValueInternal(key, value);
    }
    else {
        // make sure the previous setValue() call is executed completely before
        // starting another one...
        _updatePromise.then(function() {
            setValueInternal(key, value);
        });
    }
}

function setValueInternal(key, value) {
    _doc[key] = value;

    _updatePromise = new Promise(function(done, reject) {
        _pouch.put(_doc)
        .then(function() {
            return _pouch.get(_doc._id);
        })
        .then(function(updatedDoc) {
            _doc = updatedDoc;
            _updatePromise = null;
            done();
        })
        catch(function(error) {
            _updatePromise = null;
            reject(error);
        });
    });
}

我认为如果履行承诺(调用 done())将同步调用下一个 then() 函数,它应该可以正常工作,但我无法找到明确的答案是否是这种情况。

非常感谢任何澄清,并感谢您的帮助。

【问题讨论】:

  • 我有消息要告诉你。 Promise 并不能帮助您编写一点原子代码。事实上,它们甚至可能使事情变得更糟,因为每个.then() 处理程序都保证是异步的,这意味着在调用.then() 处理程序之前可以执行其他事情。看来您在这里的理解有些偏离基础,您的设计逻辑只是走错了方向。
  • 在更新值后真正获得原子修订 ID 的唯一方法是让数据库从写入调用返回修订 ID(将原子操作的责任放在数据库本身可以做到的地方)安全实施)。假设这是一个多用户数据库,在您读回修订 ID 之前,其他任何事情都允许对数据库进行其他更改。如果您只是想保护自己的代码不向数据库写入其他内容(而不是多用户),那么您将必须实现某种标志,所有代码都会检查。
  • @jfriend00 我知道.then() 是异步执行的,这就是为什么我想等待最后一个then()put() 的“返回”)完成后再解析@987654328 @作为一个整体。在这种情况下,数据库不是多用户的,因为它是本地 PouchDB。我只是想确保setValue() 执行以顺序方式运行。调用多次是完全没问题的,只要里面的操作按顺序排队执行即可。
  • @mrmcgreg - 我不确定这是否正确,因为当我调用done() 时,承诺已经实现,不是更早,不是吗,所以从 Promise 的构造函数中返回一些东西不会对我有任何好处。也许您可以再次检查我的代码,当我执行 put() 时,我不在 then() 内(请参阅下面的 Dark Falcon 的回复)。这当然是一个简化的例子,真实的代码将更新/检索代码提取到一个单独的方法中,其他方法调用。

标签: javascript asynchronous pouchdb


【解决方案1】:

您在这里尝试执行的链接承诺确实按预期工作,但我不相信有任何保证done 被同步调用。我认为你的代码可以工作,但你有一些反模式。我建议简化以避免明确创建承诺。

还想一想:如果您连续调用setValue 4 次,那么应该往返服务器多少次?这样做会导致需要 4 个。你想将它们分成 1 个还是 2 个?

每个setValue一个往返:

var _doc = ...;
var _pouch = new PouchDB(...);
var _updatePromise = Promise.resolve();

function setValue(key, value) {
    // make sure the previous setValue() call is executed completely before
    // starting another one...
    _updatePromise = _updatePromise.then(function() {
        _doc[key] = value;

        return _pouch.put(_doc)
        .then(function() {
            return _pouch.get(_doc._id);
        })
        .then(function(updatedDoc) {
            _doc = updatedDoc;
        });
    });
}

【讨论】:

  • 谢谢你,首先你指出了反模式,我已经改变了它,它看起来好多了。至于多个更新 - 我还有一个批量方法,允许在更新之前一次更改多个内容,这是一个本地 PouchDB,不同步到远程端点,因此不涉及往返。
猜你喜欢
  • 1970-01-01
  • 2018-11-13
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多