цепочка вызовов асинхронного вызова - javascript

У вас есть прототип объекта 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. Оберните "боб" с помощью декоратора (все еще нечеткое о том, как реализовать, можно использовать небольшой пример)

  2. Измените конструктор, чтобы назначить обратный вызов по умолчанию, если ни один не назначен (не считал, возможно ли это или нет)

  3. Использовать оболочку генератора, которая будет продолжать вызов следующего метода до тех пор, пока ничего не останется?

Ответы

Ответ 1

Более рекомендуемым способом является использование обещаний. Поскольку это общесистемная тенденция делать асинхронные материалы.

Мы хотим сделать 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());  

В любом случае, если вы не привыкли использовать обещания, вы можете прочитать это

Ответ 2

Внимание этого права еще совсем не так. В идеале мы должны были подклассировать Promise и иметь надлежащую функциональность /catch, но есть некоторые оговорки с подклассом Bluebird 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')
})

результат:

[email protected]:~/Desktop/Dropbox/code/js/async-chain$ node index.js 
bar 1492082650917
baz 1492082651511
done

Ответ 3

Если вы хотите избежать обратного вызова ада и сохранить свое здравомыслие, обещания ES6 - наиболее подходящий подход для функционального программирования. Вы просто связываете свои последовательные асинхронные задачи в асинхронной временной шкале так же, как работаете на синхронной временной шкале.

В этом конкретном случае вам просто нужно обещать асинхронные функции. Предположим, что ваши асинхронные функции берут данные и обратный вызов, например asynch(data,myCallback). Предположим, что обратный вызов является первым типом ошибки.

Такие как;

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

Когда ваша асинхронная функция будет пролонгирована, на самом деле вам будет возвращена функция, которая берет ваши данные и возвращает обещание. Вы должны применять myCallback на then этапе. Возвращаемое значение myCallback затем будет передано на следующий этап, где вы можете вызвать другую функцию asynch, поставляемую с возвращаемым значением 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)