我正在尝试按顺序执行函数的以下数组(避免使用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);
});
}
答案 0 :(得分:2)
让.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'));
答案 1 :(得分:1)
Vue.js
运行
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()
不是函数。
答案 2 :(得分:1)
您的回调函数永远不会传递参数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))