JS - 链序异步方法依次没有回调或修改 [英] JS - chain async methods in sequence w/o callback or modification
问题描述
我正在尝试向原型添加默认回调,如果没有提供回调函数,则会将异步方法的回调函数(以promise的形式)分配。
I'm trying to add a "default callback" to a prototype that will assign a callback function (in the form of a promise) to an async method if none is provided.
目标是让一个类的异步方法链同步运行
The goal is to have a class's chain of async methods run synchronously
Item.async1().async2()....asyncN()
请注意,异步函数本身期望回调但它们'在函数调用中没有作为参数传递(这告诉我在回调查找失败时类需要默认行为)
Mind you, the async functions themselves expect a callback but they're not passed as arguments in the function call (which tells me that the class needs a default behavior when the callback lookup fails)
Spec指出我无法直接修改行为或原型方法的副作用。我可以添加原型方法。我们无法了解如何实现这些原型方法。
Spec states that I cannot directly modify the behavior or side effects of the prototype methods. I can add prototype methods. We have no visibility into how these prototype methods are implemented.
TLDR:如果不修改原型方法,如何链接N个异步方法并确保它们按顺序运行?
TLDR: Without modifying the prototype methods, how can you chain an N number of async methods and ensure they run sequentially?
顺便说一句:如果我想实现promisified版本,那么宣传有问题的原型方法会有所帮助,但看起来我们受限于原始函数调用
BTW: Promisifying the prototype methods in question would be helpful if I wanted to implement the promisified versions, but it looks like we're constrained to the original function calls
推荐答案
好吧,我不打算回答 - 但我受到挑战。
Well, I wasn't going to answer - but I was challenged.
利用承诺的内置功能可以很容易地免费获得这种排队。以下是此转换的工作原理:
It's quite easy to utilize the built in abilities of promises in order to get this sort of queuing for free. Here is how this conversion will work:
- 我们将回调API转换为带有promise子类的新对象的promises。
- 我们将所有承诺的方法添加到子类本身 - 所以它链。
- 我们告诉子类执行
然后
中的所有方法,因此它们将排队为然后
是承诺的排队机制。
- We convert the callback API to promises on a new object with a promise subclass.
- We add all the promised methods to the subclass itself - so it chains.
- We tell the subclass to execute all the methods in a
then
, so they'll queue asthen
is the queueing mechanism promises have.
注意:
promisify
和promisifyAll
我在这里写的方法 - 你应该抓住NPM - 许多使用承诺构造函数的好的和快速的用法。Note: The
promisify
andpromisifyAll
methods I write here - you should grab off NPM - lots of good and fast usages that take a promise constructor.首先,我们需要一种方法将回调API转换为承诺:
First, we need a method that converts a callback API to promises:
// F is a promise subclass function promisify(fn) { // take a function return function(...args) { // return a new one with promises return new F((resolve, reject) => { // that returns a promise // that calls the original function and resolves the promise fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data)); }); }; }
现在,让我们宣传整个对象:
Now, let's promisify the whole object:
function promisifyAll(obj) { const o = {}; for(const prop in obj) { if(!(obj[prop].call)) continue; // only functions o[prop] = promisify(obj[prop]).bind(obj); } return o; }
到目前为止,没有什么新的,很多NPM库这样做 - 现在为承诺的魔力 - 让我们创建一个方法,在
中执行原始对象上的函数,然后
:So far, nothing new, lots of NPM libraries do this - now for the magic of promises - let's create a method that executes functions on an original object in a
then
:function whenReadyAll(obj) { const obj2 = {}; // create a new object for(const prop in obj) { // for each original object obj2[prop] = function(...args) { // return a function that does the same thing in a `then` return this.then(() => obj[prop](...args)); }; } return obj2; }
现在,让我们把事情搞砸
Now, let's wrap things up
function liquidate(obj) { const promised = promisifyAll(obj); // convert the object to a promise API class F extends Promise {} // create a promise subclass Object.assign(F.prototype, whenReadyAll(promised)); // add the API to it return promised; // return it // previous code here }
这就是它,如果我们希望该示例是自包含的(再次,承诺和promisifyAll通常由库提供):
And that's it, if we want the example to be self contained (again, promise and promisifyAll are provided by a library usually):
function liquidate(obj) { const promised = promisifyAll(obj); class F extends Promise {} Object.assign(F.prototype, whenReadyAll(promised)); // add the API return promised; function whenReadyAll(obj) { const obj2 = {}; for(const prop in obj) { obj2[prop] = function(...args) { return this.then(() => obj[prop](...args)); }; } return obj2; } function promisifyAll(obj) { const o = {}; for(const prop in obj) { if(!(obj[prop].call)) continue; // only functions o[prop] = promisify(obj[prop]).bind(obj); } return o; } function promisify(fn) { return function(...args) { return new F((resolve, reject) => { fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data)); }); }; } }
或者是一个提供promisify的库:
Or with a library that does promisify:
function liquidate(obj) { // 14 LoC class F extends Promise {} const promised = promisifyAll(obj, F); // F is the promise impl Object.assign(F.prototype, whenReadyAll(promised)); // add the API return promised; function whenReadyAll(obj) { const obj2 = {}; for(const prop in obj) { obj2[prop] = function(...args) { return this.then(() => obj[prop](...args)); }; } return obj2; } }
没有演示的答案是什么:
And what's an answer without a demo:
var o = { // object with a delay callback method delay(cb) { console.log("delay"); setTimeout(() => cb(null), 1000); } }; var o2 = liquidate(o); // let's liquidate it // and we even get `then` for free, so we can verify this works var p = o2.delay().then(x => console.log("First Delay!")). delay(). then(x => console.log("Second Delay!")); // logs delay, then First Delay! after a second, // then delay and then Second Delay! after a second
复制粘贴到友好的邻居控制台并亲自看看:)
Copy paste this to your friendly neighborhood console and see for yourself :)
为了证明这保留了原始对象的状态(很容易修改它而不是如果这是一个要求)让我们添加一个
我
变量并在延迟时增加它并看到一切正常:To prove this preserves state on the original object (it's easy to modify it not to if that's a requirement) let's add an
i
variable and increment it in delay and see that things work:var o = { // object with a delay callback method delay(cb) { console.log("delay", this.i++); setTimeout(() => cb(null), 1000); }, i: 0 }; var o2 = liquidate(o); // let's liquidate it // and we even get `then` for free, so we can verify this works var p = o2.delay().then(x => console.log("First Delay!")). delay(). then(x => console.log("Second Delay!", o.i)); //logs: // delay 0 // First Delay! // delay 1 // Second Delay! 2
这篇关于JS - 链序异步方法依次没有回调或修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- We tell the subclass to execute all the methods in a
- 我们告诉子类执行