多个顺序异步 JavaScript 函数 [英] Multiple Sequential Async JavaScript Functions

查看:56
本文介绍了多个顺序异步 JavaScript 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个如下所示的函数:

Let's say I have a function that looks like this:

var foo = function(callback) {
  var final = {};

  asyncFuncOne(function(x) {
    final.x = x;
  });

  asyncFuncTwo(function(y) {
    final.y = y;
  });

  callback(final);
});

显然,这并没有达到我想要的效果(当它同时具有 x 和 y 时,在 final 上调用 callback).我有几个问题:

Obviously, this doesn't do what I want it to do (call callback on final when it has both x and y). I have several questions:

  1. 有没有办法在不嵌套所有内容的情况下做我想做的事情?
  2. 当前表单是否引入了竞争条件?两个异步函数是否访问相同的 final?

推荐答案

方法 #0.没有承诺的痛苦生活.然而生活

实际上,您的代码就像要求在 Promise 中重写一样.相信我,这种重构是您 100% 需要的.但是好吧,让我们尝试在不调用 promise 的情况下解决这个特定问题 - 作为练习.其实在promise时代之前,这个模式是引入了一个特殊的函数来检查我们是否可以认为我们已经完成了.

Actually, your code like cries to be rewritten in promises. Trust me, this refactoring is something you 100% need. But ok, let's try to solve this particular problem without invoking promises at all - just as an exercise. Actually before the promise era the pattern was to introduce a special function that checks whether we can consider that we are done or not.

在您的特定情况下,此类功能是:

In your particular case such function is:

function weAreDone() {
   return final.hasOwnPropery('x') && final.hasOwnProperty('y')
}

然后我们可以引入asyncFuncDecorator:

Then we can introduce asyncFuncDecorator:

function asyncFuncDecorator = function(asyncFunc, asyncFuncHandler) {
   return function(doneFunc, doneHandler) {
       asyncFunc(asyncFuncHandler);
       if (doneFunc()) {
          doneHandler();
       }
   }
}

引入这两个函数后,您可以编写如下内容:

With this two functions introduced you can write something like:

var foo = function(callback) {
  var final = {};

  //here goes abovementioned declarations
  ... 

  asyncFuncDecorator(asyncFuncOne, function(x) {
    final.x = x;
  })(weAreDone, callback);

  asyncFuncDecorator(asyncFuncTwo, function(y) {
    final.y = y;
  })(weAreDone, callback);

});

您可以继续努力使这种方法更加灵活和通用,但再次相信我,你最终会得到与承诺非常相似的东西,所以更好的承诺;)

You can keep working on making this approach more flexible and universal but, once again, trust me, you'll end up with something very similar to promises, so better promises ;)

方法#1.承诺现有功能

如果由于某种原因,您还没有准备好将所有函数从回调样式重写为 promise,您可以再次使用装饰器来保证现有功能.以下是对所有现代浏览器中已经存在的本机 Promises 的处理方法(有关替代方案,请查看 这个问题):

If, for some reason, you are not ready to rewrite all you functions from callback style to promises, you can promisify existing functions by using, once again, a decorator. Here's how it can be done for native Promises, which are present in all modern browsers already (for alternatives, check this question):

function promisify(asyncCall){
    return new Promise(function(resolve,reject){
         asyncCall(resolve,reject);
    });
}

在这种情况下,您可以以这种方式重写代码:

In that case you can rewrite you code in this fashion:

var foo = function(callback) {

      //here goes abovementioned declarations
      ... 

      Promise.all([promisify(asyncFuncOne), promisify(asyncFuncTwo)]).then(function(data) {
          // by the way, I'd rather not to call any variable "final" ))
          final.x = data[0];
          final.y = data[1];
      }).then(callback);

    });

并不是说实际上 foo 最好是被承诺自己;)

Not to say that actually foo it's better to be promisified itself ;)

方法#2.承诺无处不在.从一开始

值得重申这个想法——一旦你需要在完成 N 个其他异步函数之后触发某个函数——在 99% 的情况下,promise 是无与伦比的.几乎总是值得尝试以基于 Promise 的风格重写现有代码.下面是这样的代码的样子

It worth to reiterate this thought - as soon as you need to trigger some function after N other async functions should be completed - promises in 99% cases are unbeatable. It almost always worth trying to rewrite existing code to in promise-based style. Here's how can such code look like

Promise.all([asyncFuncOne(), asyncFuncTwo()]).then(function(data) {

  return Promise.resolve({
    x: data[0],
    y: data[1] 
  })

}).then(callback);

看看它变得多好.此外,使用承诺的一个常见错误 - 是有一个顺序的瀑布瀑布 - 检索第一块数据,只有在那之后 - 第二个,之后 - 第三个.您实际上永远不应该这样做,除非您根据您在先前请求之一中获得的内容来转换第 N 个请求中收到的数据 - 而只是使用 all 方法.

See how much better it become. Also, a common mistake of using promises - is to have a sequential waterfall of thens - retrieving first chunk of data, only after that - the second one, after that - the third one. You actually never should do this unless you are transforming data received in Nth request depending on what you've got in one of your previous requests - instead just use all method.

理解这一点非常重要.这是承诺经常被误解为过于复杂的主要原因之一.

This is very crucial to understand. This is one of main reasons why promises quite often are misunderstood as something excessively complicated.

旁注:截至 2014 年 12 月,除 IE 之外的所有主要现代浏览器都原生支持原生 Promises,并且在 Node.js 中自 0.11.13 版本以来就具有原生 Promise 支持,所以在现实生活中你仍然是最重要的可能需要使用承诺库.有很多 Promise 规范实现,您可以查看 此页面 以获取独立的 Promise 库列表,它非常大,我猜最流行的解决方案是 Q 和 bluebird.

Sidenote: as of December'14, native Promises are natively supported by all major modern browsers except IE, and in Node.js has native promise support is a thing since version 0.11.13, so in real-life you still most probably will need to use promise library. There's a lot of Promise spec implementations, you can check this page for the list of standalone promise libraries, it's quite big, the most popular solutiona are, I guess, Q and bluebird.

方法#3.发电机.我们美好的未来.嗯,可能是

这是值得一提的,Firefox、基于 Chromium 的浏览器和 node.js(使用 --harmony_generators 选项调用)事实上支持生成器.因此,事实上,有些情况下可以在生产代码中使用并且实际上已经使用了生成器.只是如果您正在编写通用 Web 应用程序,您应该了解这种方法,但您可能暂时不会使用它.因此,您可以利用 js 中的生成器允许您通过 yield/iterator.next() 调用双向通信的事实.在那种情况下.

This is something worth to mention, generators are de-facto supported in Firefox, Chromium-based browsers and node.js (called with --harmony_generators option). So, de-facto, there are cases when generators can be used, and actually are already used, in production code. It's just that if you are writing a general-purpose web app, you should be aware of this approach but you'll probably won't use it for a while. So, you can use the fact that generators in js allow you to invoke two-way communication through yield/iterator.next(). In that case.

function async(gen) {
    var it = gen();
    var state = it.next();

    var next = function() {
        if (state.done) {
            return state.value;
        };  
        state.value(function(res) {
            state = it.next(res);   
            next();
        }); 
    }   

    next();
}

async(function* () {
    var res = { 
        x: yield asyncFuncOne,
        y: yield asyncFuncTwo
    }   

    callback(res);
});

实际上,已经有数十个库可以为您完成此生成器包装工作.您可以在此处.

Actually, there are already dozens of libraries which do this generator wrapping job for you. You can read more about this approach and related libraries here.

这篇关于多个顺序异步 JavaScript 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆