首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >顺序w/o回调或修改的JS -链异步方法

顺序w/o回调或修改的JS -链异步方法
EN

Stack Overflow用户
提问于 2016-08-19 00:29:47
回答 3查看 2.5K关注 0票数 5

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

目标是使类的异步方法链同步运行

代码语言:javascript
复制
Item.async1().async2()....asyncN()

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

规范指出,我不能直接修改原型方法的行为或副作用。我可以添加原型方法。我们无法看到这些原型方法是如何实现的。

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

顺便说一句:如果我想要实现promisified,那么对所讨论的原型方法进行将会很有帮助,但是看起来我们被限制在原始函数调用上。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-08-19 01:31:48

嗯,我不打算回答-但是我was challenged

很容易利用承诺的内在能力来获得这种免费的排队。以下是此转换的工作方式:

  • 我们将回调API转换为对一个新对象的承诺,该对象具有一个promise子类。
  • 我们将所有承诺的方法添加到子类本身--因此它会连锁。
    • 我们告诉子类执行then中的所有方法,因此它们将像then那样排队。

注意:我在这里写的 promisifypromisifyAll方法--您应该摘掉NPM --许多好的、快速的用法,都需要一个承诺的构造函数。

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

代码语言:javascript
复制
// 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));
      });
    };
  } 

现在,让我们讨论一下整个对象:

代码语言:javascript
复制
  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库都这样做--现在为了承诺的魔力--让我们创建一个方法,在then中的原始对象上执行函数

代码语言:javascript
复制
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;
  }

现在,让我们把事情总结一下

代码语言:javascript
复制
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通常是由库提供的):

代码语言:javascript
复制
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));
      });
    };
  } 
}

或者有一个图书馆,它确实可以:

代码语言:javascript
复制
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;
  }
}

没有演示的答案是什么:

代码语言:javascript
复制
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变量并在延迟中增加它,并确保一切正常运行:

代码语言:javascript
复制
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
票数 9
EN

Stack Overflow用户

发布于 2016-08-19 00:40:26

如果已经提供了.async1().async2(),并且它们需要回调,并且不允许修改它们,那么您就无法实现Item.async1().async2()....asyncN()。您调用的方法并不是以这种方式构建的,如果不允许您更改这些方法,那么除了用您想要的方法代替这些方法之外,没有什么可以做的。

如果您可以在内部使用原始方法创建具有自己名称的新方法,那么就可以这样做。如何做到这一点的一个模型是jQuery动画。他们允许你这样做:

代码语言:javascript
复制
$("#progress").slideDown(300).delay(1000).slideUp(300);

每个异步操作都将被链接在一起。jQuery通过执行以下操作实现了这一点:

  1. 每个方法都返回原始对象,因此对对象上的任何方法的链接都会工作。
  2. 如果异步操作已经在运行,那么每个被调用的新异步方法都会与该方法的参数一起进入一个队列(在对象上)。
  3. 每个方法的底层实现都可以使用带有传统回调(或许诺)的异步操作。操作完成后,它将检查队列以查看是否有更多的操作要运行,如果有,则启动队列中的下一个操作并将其从队列中移除。
  4. 如果在运行另一个异步操作时调用了新的异步操作,则再次将它们添加到队列的末尾。每个对象(或您希望序列化的任何一组项)都有自己的队列。

因此,如果期望回调的原始异步方法是.async1().async2(),则可以创建.async1Chain().async2Chain(),以便使其工作如下:

代码语言:javascript
复制
Item.async1Chain().async2Chain()....asyncNChain()

在内部,.async1Chain()将使用本地回调调用.async1(),并且该回调将被配置为检查队列以运行下一个队列操作(如果有)。

这只是解决问题的一种方法。可能还有其他人。

票数 2
EN

Stack Overflow用户

发布于 2016-11-03 10:21:24

我建议您为此使用一个库,我自己创建了一个库,它不仅允许安全链接,而且允许您使用循环和ifElse结构。

https://github.com/Raising/PromiseChain

(请注意,在本例中我们没有使用父对象,它经常清除代码)内部作用域保存每个继续的结果和提供的名称。

代码语言:javascript
复制
var internalScope = {}; //i'll use scope

new PromiseChain(internalScope )  
    .continue(function(internalScope){ return async1();},"firstResult")  
    .continue(function(internalScope){ return async2();},"secondResult")  
    .continue(function(internalScope){ return async3();},"thridResult")  
.end();

或者,如果所有函数都属于同一个对象,并且只需要将作用域作为参数,则可以这样做。

代码语言:javascript
复制
new PromiseChain(internalScope,yourObject)  // this is important if you use the 'this' keyword inside the functions, it works as a .bind(yourObject) for every function
    .continue(yourObject.async1,"firstResult")  
    .continue(yourObject.async2,"secondResult")  
    .continue(yourObject.async3,"thridResult")  
.end();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39029429

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档