JS - 链序异步方法依次没有回调或修改 [英] JS - chain async methods in sequence w/o callback or modification

查看:65
本文介绍了JS - 链序异步方法依次没有回调或修改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to add a "default callback" to a prototype that will assign a callback function (in the form of a promise) to an async method if none is provided.

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

The goal is to have a class's chain of async methods run synchronously

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

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

Mind you, the async functions themselves expect a callback but they're not passed as arguments in the function call (which tells me that the class needs a default behavior when the callback lookup fails)

Spec指出我无法直接修改行为或原型方法的副作用。我可以添加原型方法。我们无法了解如何实现这些原型方法。

Spec states that I cannot directly modify the behavior or side effects of the prototype methods. I can add prototype methods. We have no visibility into how these prototype methods are implemented.

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

TLDR: Without modifying the prototype methods, how can you chain an N number of async methods and ensure they run sequentially?

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

BTW: Promisifying the prototype methods in question would be helpful if I wanted to implement the promisified versions, but it looks like we're constrained to the original function calls

推荐答案

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

Well, I wasn't going to answer - but I was challenged.

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

It's quite easy to utilize the built in abilities of promises in order to get this sort of queuing for free. Here is how this conversion will work:


  • 我们将回调API转换为带有promise子类的新对象的promises。

  • 我们将所有承诺的方法添加到子类本身 - 所以它链。


    • 我们告诉子类执行然后中的所有方法,因此它们将排队为然后是承诺的排队机制。

    • We convert the callback API to promises on a new object with a promise subclass.
    • We add all the promised methods to the subclass itself - so it chains.
      • We tell the subclass to execute all the methods in a then, so they'll queue as then is the queueing mechanism promises have.

      注意: promisify promisifyAll 我在这里写的方法 - 你应该抓住NPM - 许多使用承诺构造函数的好的和快速的用法。

      Note: The promisify and promisifyAll methods I write here - you should grab off NPM - lots of good and fast usages that take a promise constructor.

      首先,我们需要一种方法将回调API转换为承诺

      First, we need a method that 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));
            });
          };
        } 
      

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

      Now, let's promisify the whole object:

        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库这样做 - 现在为承诺的魔力 - 让我们创建一个方法,在中执行原始对象上的函数,然后

      So far, nothing new, lots of NPM libraries do this - now for the magic of promises - let's create a method that executes functions on an original object in a 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;
        }
      

      现在,让我们把事情搞砸

      Now, let's wrap things up

      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
      }
      

      这就是它,如果我们希望该示例是自包含的(再次,承诺和promisifyAll通常由库提供):

      And that's it, if we want the example to be self contained (again, promise and promisifyAll are provided by a library usually):

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

      或者是一个提供promisify的库:

      Or with a library that does 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;
        }
      }
      

      没有演示的答案是什么:

      And what's an answer without a demo:

      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
      

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

      Copy paste this to your friendly neighborhood console and see for yourself :)

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

      To prove this preserves state on the original object (it's easy to modify it not to if that's a requirement) let's add an i variable and increment it in delay and see that things work:

      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
      

      这篇关于JS - 链序异步方法依次没有回调或修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆