Array.filter 过滤器出错:Async / Await中的冒险 [转译]

2019年6月24日17:01:55 发表评论 暂未收录

正如在软件开发中经常发生的那样,我有一些部分完成的Node.js脚本代码交给我,其中包含对我要接收和完成的现有逻辑的增强。我继续添加剩下的所需更改,以便可以测试代码。事情开始变得腥了。这是一项数据处理工作,所以我不知道它需要多长时间或者要求多大。我开始了,认为可能需要几秒钟或几分钟才开始浏览网页。我的MacBook听起来像是一架商用客机。网站没有开放。应用程序变慢了。有些事情是非常错误的。这个剧本怎么这么苛刻?

我停止了工作并开始调查。没有什么不合适的地方。我几乎没有添加任何内容。我又跑了 一样。我尝试改变我添加的逻辑。没运气。所以,我做了你想做的事情,我删除了更改并测试了原始版本。似乎很好。这可能是什么?我给出的附加代码出了什么问题?它没有太多。从数据库中获取一些数据。用标准过滤器操纵它。调用异步方法并等待它... Async / await。是不是在等?为什么不等呢?哦,不......这是一个错误。

以下是对代码中发生的变化以及最终启示的一般重构。

场景#1:给定一个对象数组,过滤到一个子集:

const values = [1, 2, 1, 2, 1, 2, 1, 2, 2, 2];
const results = values.filter(value => {
  return value == 1;
});

console.log(results);

输出:

[1, 1, 1, 1]

这有效。很普通的。预期。如果值为1,我们保留它,否则它会被丢弃。

场景#2:给定对象数组,使用函数过滤到子集:

const double = function(value) {
  return value * 2;
};

const values = [1, 2, 1, 2, 1, 2, 1, 2, 2, 2];
const results = values.filter(value => {
  let x = double(value);
  return x == 4;
});

console.log(results);

输出:

[2, 2, 2, 2, 2, 2]

这仍然按预期工作,并且不应该是标准函数调用的意外。如果值的两倍是4,我们保留它,否则它会被丢弃。

场景#3:给定对象数组,使用异步函数过滤到子集:

const double = function(value) {
  return new Promise(resolve => {
    setTimeout(function() {
      resolve(value * 2);
    }, 5000);
  });
};

const values = [1, 2, 1, 2, 1, 2, 1, 2, 2, 2];
const results = values.filter(value => {
  var x = double(value);
  return x == 4;
});

console.log(results);

输出:

[]

此代码不适用于过滤,也不起作用。如果您熟悉异步代码,那么您就可以立即了解它。函数调用的结果不会返回我们想要的值,它是一个Promise并且它弄乱了我们的条件。正在处理代码的开发人员也知道这一点。如果您自己运行此代码,您将看到输出几乎立即显示,然后在一段延迟后程序完成。这是因为即使过滤器方法已经返回,异步操作仍然会触发。

场景#4:给定对象数组,使用异步函数过滤到子集,并应用async / await:

const double = function(value) {
  return new Promise(resolve => {
    setTimeout(function() {
      resolve(value * 2);
    }, 5000);
  });
};

const values = [1, 2, 1, 2, 1, 2, 1, 2, 2, 2];
const results = values.filter(async value => {
  var x = await double(value);
  return x == 4;
});

console.log(results);

输出:

[1, 2, 1, 2, 1, 2, 1, 2, 2, 2];

咦?这是最初的价值观。延迟仍然存在。正如我当时也愿意接受的那样,开发人员认为添加async / await足以等待Promise解决。它不是你从输出中看到的。它似乎完全有不同的影响。我尝试的任何东西都不会让它发挥作用。代码正在被解释,但是调用的速度很快,因为它们无法等待最后一次完成,导致我的机器瘫痪。过滤器内部的异步对我来说似乎很奇怪,但我不确定为什么。这毕竟跑。接下来是大量的随机搜索和指数更多的拒绝,但我最终接受过滤器不支持async / await。这是一个同步功能,所以即使我误入歧途尝试在过滤器之外添加另一个等待也只是证实了我是多么天真。解释器最终生成了一个错误。

作为一个死胡同,我回归到一个不太优雅的解决方案,我接下来会描述。我绝不喜欢它,但它至少对我们来说是可读的。Happenstance,这是代码的最后一次修订,因为我们改变了我们的工作方法,所有这一切都被删除了。可能有一个干净的类似过滤器的解决方案,在某处使用async / await,但这仍然是读者的练习。

解决方案:给定对象数组,使用异步函数过滤到子集,并在for循环中应用async / await:

const double = function(value) {
  return new Promise(resolve => {
    setTimeout(function() {
      resolve(value * 2);
    }, 5000);
  });
};

const values = [1, 2, 1, 2, 1, 2, 1, 2, 2, 2];
const results = [];

const job = async () => {
  for (let value of values) {
    if ((await double(value)) == 4) {
      results.push(value);
    }
  }
};

job().then(() => {
  console.log(results);
});

输出:

[ 2, 2, 2, 2, 2, 2 ]

太好了,终于有效了。构建二级数组的传统for循环。它变慢了,这让我感到难过。

为什么过滤器不能开箱即可,等待传递异步功能,超出我的范围,但限制是真实的。在整个学习过程中,对我来说最大的震惊是,没有一个工具可以检测到误用。ESLint没有发现它。Visual Studio Code对我没有任何意见。我甚至安装了WebStorm,自称为“最聪明的JavaScript IDE”,同时写这篇文章,不幸的是没有,没有任何帮助。所以我现在和永远只能向你充满信心地宣告一件事:测试!您的!码!

weinxin
我的微信
爱生活、爱学习的小伙伴可以通过扫一扫二维码添加我的个人微信一起交流!
青青子衿

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: