链接异步方法调用 - javascript

时间:2016-08-18 23:15:46

标签: javascript asynchronous callback generator es6-promise

你有一个原型对象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);
};

我们想做bob.bar()。baz()并按顺序记录“bar”和“baz”。

如果你不能修改方法调用(包括传入你的回调函数),你如何将默认回调传递给这些方法调用?

一些想法:

  1. 用装饰器包裹“bob”(仍然模糊如何实现,可以用一个小例子)

  2. 修改构造函数以指定默认回调(如果没有分配)(未考虑是否可行)

  3. 使用生成器包装器继续调用next方法,直到没有剩下?

3 个答案:

答案 0 :(得分:8)

更推荐的方法是使用promises。因为这是社区范围内做异步事物的趋势。

  

我们想做bob.bar()。baz()并让它登录" bar"和" baz"   顺序。

为什么你要这样做只是为了实现这个bob.bar().baz()"语法"?如果你能做到这一点,只需使用Promise API,而无需额外努力使语法工作确实增加代码复杂性,使实际代码难以理解。

因此,您可能需要考虑使用基于承诺的方法,这种方法提供的灵活性远远超过您的方法所能实现的灵活性:

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);
    };
};

现在你这样做是为了一个接一个地顺序运行它们:

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());

如果您需要链接更多功能,您可以简单地执行此操作:

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

无论如何,如果您不习惯使用承诺,可能需要read this

答案 1 :(得分:4)

警告这还不是很正确。理想情况下,我们是Promise的子类,并且具有正确的then / catch功能,但是有一些关于子类bluebird Promise的警告。我们的想法是存储一个内部数组的promise生成函数,然后等待一个Promise(然后/等待)连续等待这些promise。

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')
})

结果:

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

答案 2 :(得分:0)

如果你想避免回调地狱并保持理智,ES6承诺是最适合功能编程的方法。您只需在异步时间轴中链接顺序异步任务,就像在同步时间轴中工作一样。

在这种特殊情况下,您只需要宣传您的异步函数。假设您的异步函数接受数据和回调,如asynch(data,myCallback)。我们假设回调是第一个错误类型。

如;

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

当你的异步函数被启用时,你实际上会返回一个获取数据并返回一个promise的函数。您需要在myCallback阶段申请then。然后,myCallback的返回值将被传递到下一个阶段,您可以在该阶段调用另一个带有返回值myCallback的异步函数,并且只要您需要,它就会一直打开。那么让我们看看我们将如何将这个摘要实现到您的工作流程中。

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)