繁体   English   中英

按顺序执行回调而不使用Promises

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM