我试图添加"默认回调"一个原型,如果没有提供回调函数(以promise的形式)给异步方法。
目标是让一个类的异步方法链同步运行
Item.async1().async2()....asyncN()
请注意,异步函数本身期望回调,但它们不会在函数调用中作为参数传递(这告诉我在回调查找失败时类需要默认行为)
Spec声明我无法直接修改原型方法的行为或副作用。我可以添加原型方法。我们无法了解这些原型方法的实现方式。
TLDR:如果不修改原型方法,如何链接N个异步方法并确保它们按顺序运行?
BTW:如果我想实现已宣传的版本,那么宣传有问题的原型方法会有所帮助,但看起来我们已经限制在原始函数调用
答案 0 :(得分:7)
好吧,我没有回答 - 但我was challenged。
很容易利用承诺的内置功能,以便免费获得这种排队。以下是此转换的工作原理:
then
中的所有方法,因此它们排队为then
是承诺所具有的排队机制。 注意:我在这里写的promisify
和promisifyAll
方法 - 您应该抓住NPM - 许多使用承诺构造函数的好的和快速的用法。
首先,我们需要一种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));
});
};
}
现在,让我们宣传整个对象:
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库这样做 - 现在为了承诺的魔力 - 让我们创建一个方法来执行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;
}
现在,让我们结束
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通常由一个库提供):
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));
});
};
}
}
或者使用可以宣传的图书馆:
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;
}
}
没有演示的答案是什么:
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
将此粘贴粘贴到友好的邻居控制台,亲眼看看:)
要证明这可以保留原始对象的状态(很容易将其修改为不符合要求)让我们添加i
变量并在延迟时增加它看到事情有效:
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
答案 1 :(得分:2)
如果已经提供了.async1()
和.async2()
并且他们需要回调并且您不允许修改它们,则无法实现Item.async1().async2()....asyncN()
。你正在调用的方法不是以这种方式工作而构建的,如果你不允许更改它们,那么除了用你想要的方法替换那些方法之外你没有什么可做的。
如果您可以创建具有内部使用原始方法的自己名称的新方法,那么就可以完成。一个如何做到这一点的模型是jQuery动画。他们允许你做这样的事情:
$("#progress").slideDown(300).delay(1000).slideUp(300);
每个异步操作都将链接在一起。 jQuery通过执行以下操作来实现:
因此,如果期望回调的原始异步方法是.async1()
和.async2()
,您可以创建.async1Chain()
和.async2Chain()
,这样就可以使其工作如下:
Item.async1Chain().async2Chain()....asyncNChain()
在内部,.async1Chain()
将使用本地回调调用.async1()
,并且该回调将配置为检查队列以运行下一个排队操作(如果有)。
这只是解决问题的一种方法。可能还有其他人。
答案 2 :(得分:0)
我建议你使用一个库,我自己创建一个库,不仅允许使用secuential链接,而且让你使用循环和ifElse结构。
https://github.com/Raising/PromiseChain
(请注意,在此示例中我们没有使用父对象,它会清理很多代码) 内部范围使用提供的名称保存每个继续的结果
var internalScope = {}; //i'll use scope
new PromiseChain(internalScope )
.continue(function(internalScope){ return async1();},"firstResult")
.continue(function(internalScope){ return async2();},"secondResult")
.continue(function(internalScope){ return async3();},"thridResult")
.end();
或者,如果所有函数都属于同一个对象,并且只需要作为参数的范围,则可以执行此操作
new PromiseChain(internalScope,yourObject) // this is important if you use the 'this' keyword inside the functions, it works as a .bind(yourObject) for every function
.continue(yourObject.async1,"firstResult")
.continue(yourObject.async2,"secondResult")
.continue(yourObject.async3,"thridResult")
.end();