[英]Executing callbacks in sequential order without using Promises
我试图按顺序执行函数的以下数组(避免callbackHell),以实现函数runCallbacksInSequence
(我需要实现自己的函数以了解回调的工作原理并避免使用Async.js)。 这是我到目前为止所拥有的。 我不太了解回调如何工作,这就是为什么我要进行此练习。 如果您有任何想法,请告诉我我做错了什么以及如何解决。
function first(cb) { console.log('first()'); cb(); } function second(cb) { console.log('second()'); cb(); } function third(cb) { console.log('third()'); cb(); } function last() { console.log('last()'); } let fns = [first, second, third, last]; function runCallbacksInSequence(fns, cb) { return fns.reduceRight((acc, f) => f(acc), cb); } runCallbacksInSequence(fns, second);
callbackHell
// first(function() {
// third(function() {
// second(function() {
// last();
// });
// });
// });
UPD
const cache = {};
function runCallbacksInSequence(fns, cb) {
fns.reduce(
function(r, f) {
return function(k) {
return r(function() {
if (cache[f]) {
return;
// f(function(e, x) {
// e ? cb(e) : k(x);
// });
} else {
cache[f] = f;
return f(function(e, x) {
return e ? cb(e) : k(x);
});
}
});
};
},
function(k) {
return k();
}
)(function(r) {
return cb(null, r);
});
}
让.reduce
回调是一个高阶函数,当调用该函数时,将调用该回调链中的下一个函数。 最后,您将具有一个函数链,该函数链将首先调用第一个函数,然后调用第二个函数,依此类推:
function first(cb) { console.log('first()'); cb(); } function second(cb) { console.log('second()'); cb(); } function third(cb) { console.log('third()'); cb(); } function last() { console.log('last()'); } let fns = [first, second, third, last]; function runCallbacksInSequence(fns, cb) { const chainedFns = fns.reduceRight((acc, f) => () => f(acc), cb); return chainedFns(); } runCallbacksInSequence(fns);
如果您想让runCallbacksInSequence
接受另一个在它们全部结尾处运行的回调,则:
function first(cb) { console.log('first()'); cb(); } function second(cb) { console.log('second()'); cb(); } function third(cb) { console.log('third()'); cb(); } function last(cb) { console.log('last()'); cb(); } let fns = [first, second, third, last]; function runCallbacksInSequence(fns, cb) { const chainedFns = fns.reduceRight((acc, f) => () => f(acc), cb); return chainedFns(); } runCallbacksInSequence(fns, () => console.log('outer call'));
fns.reduceRight((acc, f) => f(acc), cb)
运行
[first, second, third, last].reduceRight((acc, f) => f(acc), second)
变成
((acc, f) => f(acc))(
((acc, f) => f(acc))(
((acc, f) => f(acc))(
((acc, f) => f(acc))(
second,
last
),
third
),
second
),
first
)
(因为那是reduceRight
所做的事情)。
首先运行的是最内部的调用,
((acc, f) => f(acc))(
second,
last
)
这变成
last(second)
(根据last
的定义)
(function () { console.log('last()'); })(second)
此表达式忽略second
,将last()
写入控制台,并返回undefined
。
这使我们的表达为
((acc, f) => f(acc))(
((acc, f) => f(acc))(
((acc, f) => f(acc))(
undefined,
third
),
second
),
first
)
下一个最里面的调用是
((acc, f) => f(acc))(
undefined,
third
)
变成
third(undefined)
根据third
的定义,这相当于
(function (cb) {
console.log('third()');
cb();
})(undefined)
依次执行
console.log('third()');
undefined();
这会将third()
写入控制台,然后抛出异常,因为undefined
不是函数。
您的回调永远不会传递参数cb()
。 在实际程序中,您可能希望获得结果。 回调旨在接收某种消息-即,您回叫什么 ? 在此程序中,我们将发送一些消息,并确保将所有消息传递给最终的回调-
function first(cb) { console.log('first()') cb(1) // return some result } function second(cb) { console.log('second()') cb(2) // return some result } function third(cb) { console.log('third()') cb(3) // return some result } function last(cb) { console.log('last()') cb('last') // return some result } function runCallbacksInSequence(fns, cb) { fns.reduce ( (r, f) => k => r(acc => f(x => k([ ...acc, x ]))) , k => k([]) ) (cb) } const fns = [ first, second, third, last ] runCallbacksInSequence(fns, results => { console.log("-- DONE --") console.log(...results) })
输出是-
first()
second()
third()
last()
-- DONE --
1 2 3 'last'
对于额外的功能编程-
上面的reducer基于称为Continuation的基本数据结构。 如果提取它,我们可以更清楚地看到runCallbacksInSequence
在做什么-
function append (a = [], x = null) {
return a.concat([ x ]) // basic append operation
}
function runCallbacksInSequence(fns, cb) {
Cont.run
( fns.reduce // in the context of Cont ...
( Cont.lift2(append) // reduce using append
, Cont.of([]) // init with empty array
)
, cb
)
}
这是Cont
-
const Cont =
{ of: x =>
k => k (x)
, lift2: f => (mx, my) =>
k => mx (x => my (y => k (f (x, y))))
, run: (c, k) =>
c (k)
}
展开下面的代码片段,以在您自己的浏览器中查看结果-
function first(cb) { console.log('first()') cb(1) // return some result } function second(cb) { console.log('second()') cb(2) // return some result } function third(cb) { console.log('third()') cb(3) // return some result } function last(cb) { console.log('last()') cb('last') // return some result } const Cont = { of: x => k => k (x) , lift2: f => (mx, my) => k => mx (x => my (y => k (f (x, y)))) , run: (c, k) => c (k) } function append (a = [], x = null) { return a.concat([ x ]) } function runCallbacksInSequence(fns, cb) { Cont.run ( fns.reduce ( Cont.lift2(append) , Cont.of([]) ) , cb ) } const fns = [ first, second, third, last ] runCallbacksInSequence(fns, results => { console.log("-- DONE --") console.log(...results) })
使用reduce
不是表达此类程序的唯一方法。 编程就是为了创造自己的便利。 如果我们可以拥有下面的$
这样的直观神奇功能,该怎么办? 我们可以从一些价值开始,然后链出许多必要的步骤-
$ ([])
(andAppend(first))
(andAppend(second))
(andAppend(second))
(andAppend(third))
(andAppend(third))
(andAppend(third))
(andAppend(last))
(x => console.log ("done", x))
// first()
// second()
// second()
// third()
// third()
// third()
// last()
// "done" [ 1, 2, 2, 3, 3, 3, "last" ]
任何简单的功能都可以按顺序执行-
function progress(p) {
console.log("progress:", p)
return p
}
$ ([])
(andAppend(first))
(andAppend(second))
(progress)
(andAppend(third))
(andAppend(last))
(x => console.log ("done", x))
// first()
// second()
// progress: [ 1, 2 ]
// third()
// last()
// "done" [ 1, 2, 3, "last" ]
这似乎是使用异步功能的一种非常直观的方法。 我们只需要立即执行$
。 它能有多难?
const $ = x =>
k => $(Promise.resolve(x).then(k))
现在我们实现andAppend
function andAppend(f) {
return acc =>
new Promise(r =>
f(x => r([ ...acc, x ]))
)
}
展开下面的代码片段,以查看它在您的浏览器中的作用-
function first(cb) { console.log('first()') cb(1) } function second(cb) { console.log('second()') cb(2) } function third(cb) { console.log('third()') cb(3) } function last(cb) { console.log('last()') cb('last') } function andAppend(f) { return acc => new Promise(r => f(x => r([ ...acc, x ])) ) } function progress(p) { console.log("progress:", p) return p } const $ = x => k => $(Promise.resolve(x).then(k)) $ ([]) (andAppend(first)) (andAppend(second)) (progress) (andAppend(third)) (andAppend(last)) (x => console.log ("done", x))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.