当前位置: 首页 > 面试题库 >

JS-按顺序进行链式异步方法,无需回调或修改

秦禄
2023-03-14
问题内容

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

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

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

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

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

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

顺便说一句:如果我想实现承诺的版本,那么有问题的原型方法将很有帮助,但是看起来我们只限于原始函数调用


问题答案:

好吧,我不想回答-但是我受到了挑战。

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

  • 我们将回调API转换为具有Promise子类的新对象上的Promise。
  • 我们将所有应许的方法添加到子类自身中-从而将其链接。
    • 我们告诉子类执行中的所有方法then,因此它们将按照then承诺的排队机制进行排队。

注意: 我在这里写的promisifypromisifyAll方法-您应该使用NPM-
很多好的和快速的用法都带有一个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));
      });
    };
  }

现在,让我们来说明整个对象:

  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

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

或使用确实可以做到的图书馆:

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


 类似资料:
  • 问题内容: 我整天都在整理此问题,希望有人能帮助您确定我的问题。我已经使用Ajax在我的应用程序中创建了“异步进度回调”类型的功能。当我将功能剥离到测试应用程序中时,我得到了预期的结果。见下图: 所需功能 当我使用相同的代码将功能绑定到我的单页应用程序中时,出现了一种阻塞问题,其中所有请求仅在最后一个任务完成后才得到响应。在测试应用程序中,所有请求均会依次响应。服务器报告所有请求的状态(“待定”)

  • 问题内容: 我正在使用Node.js运行服务器,并且需要从正在运行的另一台服务器()请求数据。我需要向数据服务器发出许多请求(〜200)并收集数据(响应大小从〜20Kb到〜20Mb不等)。每个请求都是独立的,我想将响应保存为以下形式的一个巨大数组: 请注意,项目的顺序并不重要,理想情况下,它们应该以数据可用的顺序填充数组。 现在,当运行该程序时,它将显示: 现在,由于文件的大小如此可变,我期望它可

  • 本文向大家介绍Javascript异步执行不按顺序解决方案,包括了Javascript异步执行不按顺序解决方案的使用技巧和注意事项,需要的朋友参考一下 案例分析: 比如执行懒加载时候,onscroll 事件触发多次事件时候会调用多次 ajax 回调事件,由于每个事件返回先后次序并不能保证和触发前一致,所以在数据响应返回后所添加的数据顺序就很在 push 到数组上顺序不一致。 例子1: 这里的并发“

  • 问题内容: 我想以这种方式进行Ajax调用的堆栈:call(n-1)完成后,call(n)开始… 由于多种原因,我无法使用 async:false : 一些请求可能是 jsonp (最相关) 我还有其他一些可能同时起作用的ajax请求。 浏览器被阻止 我无法以这种方式链接我的请求: 因为请求的数量和参数是根据用户输入动态创建的。 一个小例子说明了我的问题。 您将看到服务器响应顺序是随机的,我要实现

  • 本文向大家介绍JS多个异步请求 按顺序执行next实现解析,包括了JS多个异步请求 按顺序执行next实现解析的使用技巧和注意事项,需要的朋友参考一下 在js里面,偶尔会遇见需要多个异步按照顺序执行请求,又不想多层嵌套,,这里和promise.all的区别在于,promise或者Jquery里面的$.when 是同时发送多个请求,一起返回,发出去的顺序是一起;这里是按照顺序发请求 首先创建一个迭代