使用ES6代理javascript的流畅异步api
所以。。。我有一些方法。每个方法都返回一个承诺使用ES6代理javascript的流畅异步api,javascript,es6-promise,fluent,es6-proxy,chainable,Javascript,Es6 Promise,Fluent,Es6 Proxy,Chainable,所以。。。我有一些方法。每个方法都返回一个承诺 myAsyncMethods: { myNavigate () { // Imagine this is returning a webdriverio promise return new Promise(function(resolve){ setTimeout(resolve, 1000); })
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);
})
}
}
我正在尝试进行端到端
测试,因此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
不是代理方法,因此它不会返回myasynchmethods
。我尝试代理,然后
,但没有结果
有什么想法吗
谢谢,devs;) 我将从yourAsyncMethods返回包装好的承诺,允许将同步和异步方法与代理混合,并以正确的顺序执行它们:
/* 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)
},
// ...
请注意两件事:
- 您可能需要使用polyfill进行反射:
- 我们需要检查代理获取处理程序中的内置符号,例如:(还有一些浏览器)
FOO.myNavigate().mySyncPropertyOrGetter.myClick().mySyncMethod().myNavigate() ...
无论如何,您确实不应该代理
然后来装饰以返回非承诺。如果每个人都这样做,对你的方法的引用就会随着每个承诺结果而泄漏。你的makeItFluent
东西不允许分支,它总是构建一个线性prom
链。避免强制赋值。Object.assign
会破坏您的代理。谢谢@Bergi,我知道这不是最好的方法。但我必须弄清楚这一点。我看不出有必要在makeItFluent
中进行分支,你能解释一下吗?我只需要返回myAsyncMethods
的代理,对吗s无论何时调用一个方法,它都会返回一个带有代理方法的承诺,因为当我执行这个东西时,我可以看到getter内部的跟踪。你把我弄糊涂了,因为它很有效。。。也许我看不出你的观点。顺便说一句:开发一个包含WeakMap的版本以避免内存泄漏——所有这些技术都是“未来JS”,所以它非常新……很好!谢谢你的时间,我试试看,然后回来;)别担心。我也需要这一块;)如果有问题请告诉我…