JS - 链序异步方法依次没有回调或修改

Ant*_*ung 4 javascript asynchronous node.js promise

我正在尝试向原型添加"默认回调",如果没有提供,则会将异步方法的回调函数(以promise的形式)分配给异步方法.

目标是让一个类的异步方法链同步运行

Item.async1().async2()....asyncN()
Run Code Online (Sandbox Code Playgroud)

请注意,异步函数本身期望回调,但它们不会在函数调用中作为参数传递(这告诉我在回调查找失败时类需要默认行为)

Spec声明我无法直接修改原型方法的行为或副作用.我可以添加原型方法.我们无法了解这些原型方法的实现方式.

TLDR:如果不修改原型方法,如何链接N个异步方法并确保它们按顺序运行?

顺便说一下:如果我想实现promisified版本,那么宣传有问题的原型方法会很有帮助,但看起来我们受限于原始的函数调用

Ben*_*aum 7

好吧,我不打算回答 - 但我受到了挑战.

利用承诺的内置功能很容易,以便免费获得这种排队.以下是此转换的工作原理:

  • 我们将回调API转换为具有promise子类的新对象的promises.
  • 我们将所有承诺的方法添加到子类本身 - 所以它链.
    • 我们告诉子类执行a中的所有方法then,因此它们将按照then承诺所具有的排队机制进行排队.

注:promisifypromisifyAll方法我写在这里-你应该抓住关NPM -很多是采取一个承诺构造又好又快用途.

首先,我们需要一个将回调API转换为promise的方法:

// F is a promise subclass
function promisify(fn) { // take a function
    return function(...args) {  // return a new one with promises
      return new F((resolve, reject) => { // that returns a promise
         // that calls the original function and resolves the promise
         fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data));
      });
    };
  } 
Run Code Online (Sandbox Code Playgroud)

现在,让我们宣传整个对象:

  function promisifyAll(obj) {
    const o = {};
    for(const prop in obj) {
      if(!(obj[prop].call)) continue; // only functions
      o[prop] = promisify(obj[prop]).bind(obj);
    }
    return o;
  }
Run Code Online (Sandbox Code Playgroud)

到目前为止,没有什么新的,很多NPM库这样做 - 现在为了承诺的魔力 - 让我们创建一个在原始对象上执行函数的方法then:

function whenReadyAll(obj) {
    const obj2 = {}; // create a new object
    for(const prop in obj) { // for each original object
       obj2[prop] = function(...args) { 
         // return a function that does the same thing in a `then`
         return this.then(() => obj[prop](...args));
       };
    }
    return obj2;
  }
Run Code Online (Sandbox Code Playgroud)

现在,让我们把事情搞定

function liquidate(obj) {
  const promised = promisifyAll(obj); // convert the object to a promise API
  class F extends Promise {} // create a promise subclass
  Object.assign(F.prototype, whenReadyAll(promised)); // add the API to it
  return promised; // return it
  // previous code here
}
Run Code Online (Sandbox Code Playgroud)

就是这样,如果我们希望这个例子是自包含的(再次,promise和promisifyAll通常由库提供):

function liquidate(obj) {
  const promised = promisifyAll(obj);
  class F extends Promise {}
  Object.assign(F.prototype, whenReadyAll(promised)); // add the API  
  return promised;
  function whenReadyAll(obj) {
    const obj2 = {};
    for(const prop in obj) {
       obj2[prop] = function(...args) { 
         return this.then(() => obj[prop](...args));
       };
    }
    return obj2;
  }
  function promisifyAll(obj) {
    const o = {};
    for(const prop in obj) {
      if(!(obj[prop].call)) continue; // only functions
      o[prop] = promisify(obj[prop]).bind(obj);
    }
    return o;
  }
  function promisify(fn) {
    return function(...args) { 
      return new F((resolve, reject) => {
         fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data));
      });
    };
  } 
}
Run Code Online (Sandbox Code Playgroud)

或者使用具有promisify的库:

function liquidate(obj) { // 14 LoC
  class F extends Promise {} 
  const promised = promisifyAll(obj, F); // F is the promise impl
  Object.assign(F.prototype, whenReadyAll(promised)); // add the API  
  return promised;
  function whenReadyAll(obj) {
    const obj2 = {};
    for(const prop in obj) {
       obj2[prop] = function(...args) { 
         return this.then(() => obj[prop](...args));
       };
    }
    return obj2;
  }
}
Run Code Online (Sandbox Code Playgroud)

没有演示的答案是什么:

var o = {  // object with a delay callback method
  delay(cb) { 
    console.log("delay"); 
    setTimeout(() => cb(null), 1000); 
  }
};
var o2 = liquidate(o); // let's liquidate it
// and we even get `then` for free, so we can verify this works
var p = o2.delay().then(x => console.log("First Delay!")).
                   delay().
                   then(x => console.log("Second Delay!"));

// logs delay, then First Delay! after a second, 
// then delay and then Second Delay! after a second
Run Code Online (Sandbox Code Playgroud)

复制粘贴到友好的邻居控制台,看看自己:)


为了证明这保留原始对象的状态(很容易修改它而不是如果这是一个要求)让我们添加一个i变量并在延迟时增加它并看到事情有效:

var o = {  // object with a delay callback method
  delay(cb) { 
    console.log("delay", this.i++); 
    setTimeout(() => cb(null), 1000); 
  },
  i: 0
};
var o2 = liquidate(o); // let's liquidate it
// and we even get `then` for free, so we can verify this works
var p = o2.delay().then(x => console.log("First Delay!")).
                   delay().
                   then(x => console.log("Second Delay!", o.i));
//logs:
// delay 0
// First Delay!
// delay 1
// Second Delay! 2
Run Code Online (Sandbox Code Playgroud)