javascript - JS - 按顺序链接异步方法,不带回调或修改

标签 javascript node.js asynchronous promise

我正在尝试向原型(prototype)添加一个“默认回调”,如果未提供回调函数,该原型(prototype)会将回调函数(以 promise 的形式)分配给异步方法。

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

Item.async1().async2()....asyncN()

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

规范声明我不能直接修改原型(prototype)方法的行为或副作用。我可以添加原型(prototype)方法。我们无法了解这些原型(prototype)方法是如何实现的。

TLDR:在不修改原型(prototype)方法的情况下,如何链接 N 个异步方法并确保它们按顺序运行?

顺便说一句:如果我想实现已 promise 的版本,那么 promise 所讨论的原型(prototype)方法会很有帮助,但看起来我们仅限于原始函数调用

最佳答案

好吧,我不打算回答 - 但我 was challenged .

利用 promises 的内置功能来免费获得这种排队非常容易。以下是此转换的工作方式:

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

注意:我在这里写的promisifypromisifyAll 方法——你应该从 NPM 中获取——很多好的和快速的用法需要一个 promise 构造函数。

首先,我们需要一个方法 converts a callback API to promises :

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

现在,让我们 promise 整个对象:

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

到目前为止,没有什么新鲜事,很多 NPM 库都这样做 - 现在为了 promises 的魔力 - 让我们创建一个方法,在 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;
  }

现在,让我们总结一下

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
}

就是这样,如果我们希望示例是自包含的(同样,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));
      });
    };
  } 
}

或者使用 promise 的库:

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

没有演示的答案是什么:

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

将其复制粘贴到您友好的社区控制台并亲自查看 :)


为了证明这保留了原始对象的状态(很容易修改它,而不是如果这是一个要求)让我们添加一个 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

关于javascript - JS - 按顺序链接异步方法,不带回调或修改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39029429/

相关文章:

javascript - 将 JavaScript 对象属性附加到另一个对象

node.js - 是否可以在 Meteor 中使用 React 中的 Onsen UI 2

c++ - 在异步模式下使用 FtpFindFirstFile unicode 版本的访问冲突

java - 为什么没有 asyncContext.cancel()

c# - 对传递给 Task.WhenAll() 的任务使用 ConfigureAwait(false) 失败

php - 下载功能不起作用

javascript - jQuery - 如果不存在则将更改事件附加到输入元素

javascript - 撇号在 JSON 字符串数组中转义

javascript - 使用 phantomJS,等待按钮点击效果完成后再继续

javascript - 如何在 Nodejs 中使用 Promises 进行同步 http 调用