使用 Promise 在 Javascript 中创建 "atomic" 代码块

Using a Promise to create "atomic" blocks of code in Javascript

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

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

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 写入数据库之前,在它被再次读取之前没有设置其他键。是否 (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);
        });
    });
}

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

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

您在此处尝试执行的链接承诺确实按预期工作,但我不相信可以保证 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;
        });
    });
}