3

所以...我有一些方法。每个方法都返回一个承诺。

     myAsyncMethods: {
          myNavigate () {
            // Imagine this is returning a webdriverio promise
            return new Promise(function(resolve){ 
              setTimeout(resolve, 1000);
            })
          },
          myClick () {
            // Imagine this is returning a webdriverio promise
            return new Promise(function(resolve){
              setTimeout(resolve, 2000);
            })
          }
        }

我正在尝试进行end to end测试,因此prom链必须是线性的(第一次单击,下一次导航等)

目前,我可以做到这一点...

makeItFluent(myAsyncMethods)
    .myNavigate()
    .myClick()
    .then(() => myAsyncMethods.otherMethod())
    .then(() => /*do other stuff*/ )

...具有 ES6 代理功能:

function makeItFluent (actions) {
    let prom = Promise.resolve();
    const builder = new Proxy(actions, {
        get (target, propKey) {
            const origMethod = target[propKey];

            return function continueBuilding (...args) {
                // keep chaining promises
                prom = prom.then(() => (typeof origMethod === 'function') && origMethod(...args));

                // return an augmented promise with proxied object
                return Object.assign(prom, builder);
            };
        }
    });

    return builder;
};

但是,我不能做的是以下事情:

makeItFluent(myAsyncMethods)
    .myNavigate()
    .myClick()
    .then(() => myAsyncMethods.otherMethod())
    .then(() => /*do other stuff*/ )
    .myNavigate()

因为then不是代理方法,因此它不会返回myAsyncMethods。我尝试代理then但没有结果。

任何想法?

感谢开发者;)

4

2 回答 2

2

我会从 yourAsyncMethods 返回包装好的 Promises,它允许将同步和异步方法与 Proxy 和Reflect混合并以正确的顺序执行它们:

/* WRAP PROMISE */
let handlers;
const wrap = function (target) {
    if (typeof target === 'object' && target && typeof target.then === 'function') {
        // The target needs to be stored internally as a function, so that it can use
        // the `apply` and `construct` handlers.
        var targetFunc = function () { return target; };
        targetFunc._promise_chain_cache = Object.create(null);
        return new Proxy(targetFunc, handlers);
    }
    return target;
};
// original was written in TS > 2.5, you might need a polyfill :
if (typeof Reflect === 'undefined') {
    require('harmony-reflect');
}
handlers = {
    get: function (target, property) {
        if (property === 'inspect') {
            return function () { return '[chainable Promise]'; };
        }
        if (property === '_raw') {
            return target();
        }
        if (typeof property === 'symbol') {
            return target()[property];
        }
        // If the Promise itself has the property ('then', 'catch', etc.), return the
        // property itself, bound to the target.
        // However, wrap the result of calling this function.
        // This allows wrappedPromise.then(something) to also be wrapped.
        if (property in target()) {
            const isFn = typeof target()[property] === 'function';
            if (property !== 'constructor' && !property.startsWith('_') && isFn) {
                return function () {
                    return wrap(target()[property].apply(target(), arguments));
                };
            }
            return target()[property];
        }
        // If the property has a value in the cache, use that value.
        if (Object.prototype.hasOwnProperty.call(target._promise_chain_cache, property)) {
            return target._promise_chain_cache[property];
        }
        // If the Promise library allows synchronous inspection (bluebird, etc.),
        // ensure that properties of resolved
        // Promises are also resolved immediately.
        const isValueFn = typeof target().value === 'function';
        if (target().isFulfilled && target().isFulfilled() && isValueFn) {
            return wrap(target().constructor.resolve(target().value()[property]));
        }
        // Otherwise, return a promise for that property.
        // Store it in the cache so that subsequent references to that property
        // will return the same promise.
        target._promise_chain_cache[property] = wrap(target().then(function (result) {
            if (result && (typeof result === 'object' || typeof result === 'function')) {
                return wrap(result[property]);
            }
            const _p = `"${property}" of "${result}".`;
            throw new TypeError(`Promise chain rejection: Cannot read property ${_p}`);
        }));
        return target._promise_chain_cache[property];
    },
    apply: function (target, thisArg, args) {
        // If the wrapped Promise is called, return a Promise that calls the result
        return wrap(target().constructor.all([target(), thisArg]).then(function (results) {
            if (typeof results[0] === 'function') {
                return wrap(Reflect.apply(results[0], results[1], args));
            }
            throw new TypeError(`Promise chain rejection: Attempted to call ${results[0]}` +
                ' which is not a function.');
        }));
    },
    construct: function (target, args) {
        return wrap(target().then(function (result) {
            return wrap(Reflect.construct(result, args));
        }));
    }
};
// Make sure all other references to the proxied object refer to the promise itself,
// not the function wrapping it
Object.getOwnPropertyNames(Reflect).forEach(function (handler) {
    handlers[handler] = handlers[handler] || function (target, arg1, arg2, arg3) {
        return Reflect[handler](target(), arg1, arg2, arg3);
    };
});

您可以将它与您的方法一起使用,例如

myAsyncMethods: {
      myNavigate () {
        // Imagine this is returning a webdriverio promise
        var myPromise = new Promise(function(resolve){ 
          setTimeout(resolve, 1000);
        });
        return wrap(myPromise)
      },
// ...

注意两点:

你现在可以像这样混合它

FOO.myNavigate().mySyncPropertyOrGetter.myClick().mySyncMethod().myNavigate() ...
于 2017-11-02T08:35:47.917 回答
0

https://michaelzanggl.com/articles/end-of-chain/

Promise 只不过是一个符合规范的“thenable”(带有 then() 方法的对象)。而 await 只是对 Promise 的封装,以提供更简洁、更简洁的语法。

class NiceClass {
  promises = [];

  doOne = () => {
    this.promises.push(new Promise((resolve, reject) => {
        this.one = 1;
        resolve();
    }));
    return this;
  }

  doTwo = () => {
    this.promises.push(new Promise((resolve, reject) => {
      this.two = 2;
  
      resolve();
    }));

    return this;
  }

  async then(resolve, reject) {
    let results = await Promise.all(this.promises);
    resolve(results);
  }

  build = () => {
    return Promise.all(this.promises)
  }
}

他们可以通过两种方式调用。

(async () => {
  try {
    let nice = new NiceClass();
    let result = await nice
      .doOne()
      .doTwo();

    console.log(nice);

    let nice2 = new NiceClass();
    let result2 = await nice2
      .doOne()
      .doTwo()
      .build();

    console.log(nice2, result2);
  } catch(error) {
    console.log('Promise error', error);
  }
})();
于 2021-09-10T21:22:53.533 回答