简体   繁体   English

链接异步方法调用 - javascript

[英]chaining async method calls - javascript

You have a prototype object Foo with two async method calls, bar and baz.你有一个原型对象 Foo 有两个异步方法调用,bar 和 baz。

var bob = new Foo()

Foo.prototype.bar = function land(callback) {
  setTimeout(function() {
    callback()
    console.log('bar');
  }, 3000);
};

Foo.prototype.baz = function land(callback) {
  setTimeout(function() {
    callback()
    console.log('baz');
  }, 3000);
};

We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.我们想要做 bob.bar().baz() 并让它按顺序记录“bar”和“baz”。

If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?如果您无法修改方法调用(包括传入您的回调函数),您如何将默认回调传递到这些方法调用中?

Some ideas:一些想法:

  1. Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)用装饰器包裹“bob”(关于如何实现仍然模糊,可以使用一个小例子)

  2. Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)如果没有分配,则修改构造函数以分配默认回调(尚未考虑是否可能)

  3. Use a generator wrapper that will continue to call next method until none are left?使用一个生成器包装器,它将继续调用 next 方法,直到没有方法为止?

The more recommended way instead is to use promises as this is the community-wide practice to do async work.更推荐的方法是使用承诺,因为这是进行异步工作的社区范围的做法。

We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.我们想要做 bob.bar().baz() 并让它按顺序记录“bar”和“baz”。

Why would you want to do that just to achieve this bob.bar().baz() "syntax"?你为什么要这样做只是为了实现这个bob.bar().baz() “语法”? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.您可以使用 Promise API 非常巧妙地完成它,而无需额外的努力来使该语法起作用,这确实增加了代码复杂性,从而降低了实际可读性。

So, you might want to consider using the promise-based approach like this.因此,您可能需要考虑使用这样的基于 Promise 的方法。 It offers much flexibility than what you would have achieved with your approach:它提供了比您的方法所能实现的更大的灵活性:

Foo.prototype.bar = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('bar');
        }, 3000);
    };
};

Foo.prototype.baz = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('baz');
        }, 3000);
    };
};

Now you'd do this to run them sequentially one after another:现在,您将执行此操作以依次运行它们:

var bob = new Foo();

bob.bar().then(function() {
   return bob.baz();
});

// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());

If you need to chain more functions you could simply do it:如果您需要链接更多功能,您可以简单地这样做:

bob.bar()
    .then(() => bob.baz())
    .then(() => bob.anotherBaz())
    .then(() => bob.somethingElse());  

Anyway, if you're not used to using promises you might want to read this无论如何,如果您不习惯使用 Promise,您可能需要阅读此内容

Warning this isn't quite right yet.警告这还不完全正确。 Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise .理想情况下,我们将 Promise 子类化并具有适当的 then/catch 功能,但是子类化bluebird Promise有一些注意事项。 The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.这个想法是存储一个内部的承诺生成函数数组,然后当一个承诺被等待(然后/等待)时,连续等待这些承诺。

const Promise = require('bluebird');

class Foo {
  constructor() {
    this.queue = [];
  }

  // promise generating function simply returns called pGen
  pFunc(i,pGen) {
    return pGen();
  }

  bar() {
    const _bar = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('bar',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.push(_bar);
    return this;
  }

  baz() {
    const _baz = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('baz',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.push(_baz);
    return this;
  }

  then(func) {
    return Promise.reduce(this.queue, this.pFunc, 0).then(func);
  }
}


const foo = new Foo();
foo.bar().baz().then( () => {
  console.log('done')
})

result:结果:

messel@messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js 
bar 1492082650917
baz 1492082651511
done

If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming.如果你想避免回调地狱并保持理智,ES6 承诺是最适合函数式编程的方法。 You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.您只需将异步时间线中的顺序异步任务链接起来,就像在同步时间线中工作一样。

In this particular case you just need to promisify your asynchronous functions.在这种特殊情况下,您只需要承诺您的异步功能。 Assume that your asynch functions takes a data and a callback like asynch(data,myCallback) .假设您的异步函数接受一个数据和一个回调,如asynch(data,myCallback) Let us assume that the callback is error first type.让我们假设回调是错误优先类型。

Such as;比如;

var myCallback = (error,result) => error ? doErrorAction(error)
                                         : doNormalAction(result)

When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise.当您的异步函数被承诺时,您实际上将返回一个函数,该函数接受您的数据并返回一个承诺。 You are expected to apply myCallback at the then stage.myCallbackthen阶段应用myCallback The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need.然后myCallback的返回值将被传递到下一个阶段,在那里您可以调用另一个异步函数,该函数提供myCallback的返回值,并且只要您需要,这就会一直持续下去。 So let's see how we shall implement this abstract to your workflow.那么让我们看看我们将如何将这个抽象实现到您的工作流程中。

 function Foo(){} function promisify(fun){ return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res))); } function myCallback(val) { console.log("hey..! i've got this:",val); return val; } var bob = new Foo(); Foo.prototype.bar = function land(value, callback) { setTimeout(function() { callback(false,value*2); // no error returned but value doubled and supplied to callback console.log('bar'); }, 1000); }; Foo.prototype.baz = function land(value, callback) { setTimeout(function() { callback(false,value*2); // no error returned but value doubled and supplied to callback console.log('baz'); }, 1000); }; Foo.prototype.bar = promisify(Foo.prototype.bar); Foo.prototype.baz = promisify(Foo.prototype.baz); bob.bar(1) .then(myCallback) .then(bob.baz) .then(myCallback)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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