我可以在 Parse JavaScript SDK 中使用其他 promise 实现吗?

Can I use other promise implementations in the Parse JavaScript SDK?

我对我看到的使用 JQuery 兼容承诺的 Parse 的引用感到担忧,因为我已经读到 jQuery 承诺 allow consumers to mutate the state of the promise. Is it possible to use another promise implementation that is known to be Promises/A+ compliant (e.g. the ECMAScript 6 implementation, or Bluebird) 与 Parse JavaScript软件开发工具包?

通常我认为这是不可能的,但是在 Parse JavaScript SDK 的 v1.4.2 中,Parse.Promise 的实现将 属性 “_isPromisesAPlusCompliant” 定义为false 然后在库中的各种函数中检查。

N.B。这个问题是 originally asked on the Parse Developers group,但没有收到任何回复。

你可以使用原生的 Promises,或者一个好的 polyfill。你可以在 Promise.resolve 调用中封装任何 thenable(一个带有 public then 方法的类 Promise 对象),像这样:

var promise = Promise.resolve($.getJSON("/something.json"));

这也将有一个 then 方法,但不会让人头疼。它应该仍然有效。

一个选项是将 Parse SDK 原型修改为 return 不同类型的 Promise。

这个库 https://github.com/brandid/parse-angular-patch/blob/master/src/parse-angular.js 是一个很好的起点,它将 Parse 原型修补为 return AngularJS promises

            // Keep a handy local reference
            var Parse = $window.Parse;

            //-------------------------------------
            // Structured object of what we need to update
            //-------------------------------------

            var methodsToUpdate = {
                "Object": {
                    prototype: ['save', 'fetch', 'destroy'],
                    static: ['saveAll', 'destroyAll']
                },
                "Collection": {
                    prototype: ['fetch'],
                    static: []
                },
                "Query": {
                    prototype: ['find', 'first', 'count', 'get'],
                    static: []
                },
                "Cloud": {
                    prototype: [],
                    static: ['run']
                },
                "User": {
                    prototype: ['signUp'],
                    static: ['requestPasswordReset', 'logIn']
                },
                "FacebookUtils": {
                    prototype: [],
                    static: ['logIn', 'link', 'unlink']
                },
                "Config": {
                    prototype: [],
                    static: ['get']
                }
            };

            //// Let's loop over Parse objects
            for (var k in methodsToUpdate) {

                var currentClass = k;
                var currentObject = methodsToUpdate[k];

                var currentProtoMethods = currentObject.prototype;
                var currentStaticMethods = currentObject.static;


                /// Patching prototypes
                currentProtoMethods.forEach(function(method){

                    var origMethod = Parse[currentClass].prototype[method];

                    // Overwrite original function by wrapping it with $q
                    Parse[currentClass].prototype[method] = function() {

                        return origMethod.apply(this, arguments)
                        .then(function(data){
                            var defer = $q.defer();
                            defer.resolve(data);
                            return defer.promise;
                        }, function(err){
                            var defer = $q.defer();
                            defer.reject(err);
                            return defer.promise;
                        });


                    };

                });


                ///Patching static methods too
                currentStaticMethods.forEach(function(method){

                    var origMethod = Parse[currentClass][method];

                    // Overwrite original function by wrapping it with $q
                    Parse[currentClass][method] = function() {

                        return origMethod.apply(this, arguments)
                        .then(function(data){
                            var defer = $q.defer();
                            defer.resolve(data);
                            return defer.promise;
                        }, function(err){
                            var defer = $q.defer();
                            defer.reject(err);
                            return defer.promise;
                        });

                    };

                });


            }

I am concerned that Parse uses jQuery-compatible promises, as I have read that jQuery promises allow consumers to mutate the state of the promise.

你不必担心。 "jQuery-compatible" 可能意味着很多事情,并且 Parse 承诺当然不允许消费者改变他们的状态 1(因为 jQuery 多年来也没有这样做).顺便说一句,他们也是 A+ "compatible" :-)

1:通过public方法。所以不超过大多数其他实现,即。

Is it possible to use another promise implementation that is known to be Promises/A+ compliant with the Parse JavaScript SDK?

是的。 Parse SDK 确实 return 有效 A+ thenables,这意味着您可以 return 从您最喜欢的 promise 实现的 then 回调中解析 promises 并期待它完美工作:

myCompliantPromise.then(function(res) {
    return parse_query.get(…);
}).then(…)

您还可以使用 Promise.resolve 将它们转换为您实施的有效承诺,例如:

Promise.resolve(parse_query.get(…)).then(…);

Normally I would assume that this isn’t possible, but in v1.4.2 of the Parse JavaScript SDK, the implementation of Parse.Promise defines the property _isPromisesAPlusCompliant as false which is then checked in various functions within the library.

他!虽然不幸的是没有记录,这个标志实际上允许你 使 在你的应用程序中兼容本机 Parse.com promise 库 A+:

Parse.Promise._isPromisesAPlusCompliant = true;

Update:在较新的版本中,这不会显示为带下划线的 属性,而是您必须调用(未记录的)Parse.Promise.enableAPlusCompliant()方法。有关详细信息,请参阅 issue #57

我查看了 the code,这个标志基本上改变了 3 件事:

  • then 回调中的异常被捕获并导致拒绝结果承诺,而不是全局错误。所以你可以在其中使用throw
  • 如果你 return 来自 onRejected 回调的值(then 的第二个参数),错误应该被处理并且结果承诺被履行而不是被拒绝.
  • 所有 then 回调异步执行。

这些确实解决了当前problems inherent to the jQuery Deferred implementation

我假设 Parse 计划静默迁移这个 true 设置成为默认设置,并且正在测试它是否会破坏用户的任何东西。我想即使尚未记录,使用起来也相当安全。

I'd like to make all Parse APIs return promises of my custom library.

这不是那么简单,尽管可以做到。基本上有两种方法:

  • 装饰 API 中的所有 promise-returning 方法,方法是将它们与 Promise.resolve 组合,这基本上是@dancamper 建议的
  • 用库的包装器覆盖 Parse.Promise

第二个似乎更高效和稳定,它更易于维护,因为它不需要在 Parse 更改它们的 API.

时进行调整
Parse.Promise = (function(oldPromise, Promise) {
    function promise() {
        var res, rej;
        var p = new Promise(function(_res, _rej) {
            res = _res;
            rej = _rej;
        });
        p.resolve = res;
        p.reject = rej;
        return p;
    }
    promise.is = oldPromise.is;
    promise.as = Promise.resolve;
    promise.error = Promise.reject;
    promise.when = Promise.all; // ²
    promise._continueWhile = oldPromise._continueWhile;
    Promise.prototype._continueWith = oldPromise.prototype._continueWith;
    Promise.prototype._thenRunCallback = oldPromise.prototype._thenRunCallback;

    // you might not need / want these ³
    Promise.prototype.always = oldPromise.prototype.always;
    Promise.prototype.done = oldPromise.prototype.done; 
    Promise.prototype.fail = oldPromise.prototype.fail;

    return promise;
}(Parse.Promise, require("Bluebird"))); // or whatever

2:Promise.all 解析为数组,而 Parse.Promise.when 解析为多个参数(见下文)。您可能想要/需要保留它并改用 promise.when = oldPromise.when;
3:确保不要在此处覆盖自定义库的方法。 Parse 不需要这些方法,它们是为了 jQuery 兼容性。

请注意,Parse 确实像 jQuery 一样,有时会使用多个值解决其承诺,例如在 Parse._ajax。它内部不依赖此功能,但您应该检查您最喜欢的 promise 库如何处理它们。