1. 主页
  2. 文档
  3. 学习君土脚本
  4. 异步君土脚本
  5. 选择正确的方法

选择正确的方法

为了完成这个模块,我们将简要讨论之前章节谈论过编码技术和功能,看看你应该使用哪一个,并提供适当的建议和提醒。随着时间的推移,我们可能会增加这个资源。

异步回调

通常在旧式编程接口中找到,涉及将函数作为参数传递给另一个函数,然后在异步操作完成时调用该函数,以便回调可以依次对结果执行某些操作。这是承诺的前身;它不那么高效或灵活。仅在必要时使用。

代码示例

通过秘传.取()加载资源的示例:

引 * 作 秘传 自 '秘传';

务 加载象谱(网址: 文, 回调: (错误: 化, 对象?: 化) => 无) {
  秘传.取(网址, (应答) => {
    若 (应答.状码 != 200) {
      回调(启 错('错误状态码:' + 应答.状码));
      回;
    }
    应答.置编码('码8');
    定 接收数组: 文[] = [];
    应答.对('数据', 块 => 接收数组.压(块));
    应答.对('结束', () => {
      定 接收字符串 = 接收数组.串();
      定 接收象谱 = {};
      试 {
        接收象谱 = 象谱.析(接收字符串.转字());
        回调(空, 接收象谱);
      } 接 (错误) {
        回调(错误);
      }
    });
  });
}
务 显示文本(错误: 化, 对象: 化) {
  若 (错误) {
    控制台.日志(错误);
  } 别 {
    控制台.日志(象谱.串(对象));
  }
}
定 网址 = 'https://git.jtu.net.cn/xuexi/shuru/-/raw/master/书.json';
加载象谱(网址, 显示文本);

缺陷

  • 嵌套回调可能很麻烦且难以阅读(即“回调地狱”)
  • 每层嵌套都需要故障回调,而使用承诺,您只需使用一个.接()代码块来处理整个链的错误。
  • 异步回调不是很优雅。
  • 承诺回调总是按照它们放在事件队列中的严格顺序调用;异步回调不是。
  • 当传入到一个第三方库时,异步回调对函数如何执行失去完全控制。

设置超时

设置超时/*setTimeout*/() 是一种允许您在经过任意时间后运行函数的方法

代码示例

在下面的示例中,脚本运行环境将在执行匿名函数之前等待两秒钟,然后显示在控制台显示消息:

定 问候 = 设置超时(务() {
  控制台.日志('你好, 星辰大海!');
}, 2000);

缺陷

您可以使用递归的设置超时()调用以类似于设置间隔()的方式重复运行函数,使用如下代码:

  定 甲 = 1;

  设置超时(务 运行() {
    控制台.日志(甲);
    甲++;
    设置超时(运行, 100);
  }, 100);

递归设置超时()设置间隔()之间存在差异:

  • 递归设置超时()保证两次执行间经过指定的时间量(在本例中为100毫秒);代码将运行,然后等待100毫秒再次运行。无论代码运行多长时间,间隔都是相同的。
  • 使用设置间隔(),我们选择的时间间隔包含了运行代码所花费的时间。(还是100毫秒为例)假设代码需要40毫秒才能运行 –– 间隔最终只会有60毫秒。

当你的代码有可能比你分配的时间间隔更长时间运行时,最好使用递归的设置超时() ––这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。

设置间隔

设置间隔/*setInterval*/()函数允许重复执行一个函数,并设置时间间隔。

代码示例

以下函数创建一个新的历()对象,使用转时文/*toLocaleTimeString*/()从中提取时间字符串,然后在界面中显示它。然后我们使用设置间隔()每秒运行一次,创建每秒更新一次的数字时钟的效果:

务 显示时间() {
  定 日期 = 启 历();
  定 时间 = 日期.转时文();
  控制台.日志(时间);
}

常 创建钟表 = 设置间隔(显示时间, 1000);

缺陷

  • 帧速率未针对运行动画的系统进行优化,并且可能效率低下。

承诺

诺承 是一种脚本功能,允许您运行异步操作并等到它完全完成后再根据其结果运行另一个操作。 诺是现代异步脚本的支柱。

代码示例

以下代码从服务器获取数据并将其显示在控制台:

引 阿修斯 自 '阿修斯';

定 网址 = `https://git.jtu.net.cn/xuexi/shuru/-/raw/master/${编码地址('书')}.json`;

阿修斯.取(网址).下(务(应答) {
  若(应答.状态 != 200) {
    抛 启 错('读取数据出错:' + 应答.状态);
  } 别 {
    回 应答.数据
  }
}).下(务(数据) {
  控制台.日志(象谱.串(数据));
}).接(务(错误) {
  控制台.日志(错误);
});

缺陷

承诺链可能很复杂,难以解析。如果你嵌套了许多承诺,你最终可能会遇到类似的麻烦来回调地狱。例如:

远程数据库.所有文档({
  包含文档: 真,
  附件: 真
}).下(务 (结果) {
  定 文档:[] = 结果.行;
  文档.每(务(项) {
    本地数据库.改(项.文档).下(务(应答)) {
      控制台.日志("获得文档" + 项.文档.标识 + " 并且添加到本地数据库。");
    }).接(务 (错) {
      若 (错.名 == '冲突') {
        本地数据库.读(项.文档.标识).下(务 (应答1) {
          本地数据库.删(应答1.标识, 应答1.数据).下(务 (应答2) {
// 等等...

最好使用承诺的链功能,这样使用更平顺,更易于解析的结构:

远程数据库.所有文档(...).下(务 (所有文档) {
  回 本地数据库.改(...);
}).下(务 (修改结果) {
  回 本地数据库.读(...);
}).下(务 (读取结果) {
  回 本地数据库.改(...);
}).接(务 (错误) {
  控制台.日志(错误);
});

乃至:

远程数据库.所有文档(...)
  .下(所有文档 => {
    回 本地数据库.改(...);
  }).下(修改结果 => {
    回 本地数据库.读(...);
  }).下(读取结果 => {
    回 本地数据库.改(...);
  }).接(错误 => {
    控制台.日志(错误);
  });

这涵盖了很多基础知识。

诺.全

一种脚本功能,允许您等待多个承诺完成,然后根据所有其他承诺的结果运行进一步的操作。

代码示例

以下示例从服务器获取多个资源,并使用诺/*Promise*/.全/*all*/()等待所有资源可用,然后显示所有这些资源:

引 阿修斯 自 '阿修斯';

定 网址1 = `https://git.jtu.net.cn/xuexi/shuru/-/raw/master/${编码地址('书')}.json`;
定 网址2 = `https://git.jtu.net.cn/xuexi/shuru/-/raw/master/${编码地址('书2')}.json`;
定 网址3 = `https://git.jtu.net.cn/xuexi/shuru/-/raw/master/${编码地址('书3')}.json`;

定 书1 = 阿修斯.取(网址1);
定 书2 = 阿修斯.取(网址2);
定 书3 = 阿修斯.取(网址3);

诺.全([书1, 书2, 书3]).下(值数组 => {
  定 回应0 = 值数组[0].数据;
  定 回应1 = 值数组[1].数据;
  定 回应2 = 值数组[2].数据;
  控制台.日志(象谱.串(回应0));
  控制台.日志(象谱.串(回应1));
  控制台.日志(象谱.串(回应2));
}).接((错误: 错) => {
  控制台.日志('错误:' + 错误.信息);
});

缺陷

  • 如果诺.全()拒绝,那么你在其数组参数中输入的一个或多个承诺就会被拒绝,或者可能根本不返回承诺。你需要检查每一个,看看他们返回了什么。

途/等

构造在承诺之上的语法糖,允许您使用更像编写同步回调代码的语法来运行异步操作。

代码示例

以下示例是我们之前看到的简单承诺示例的重构,该示例获取并显示图像,使用途 / 等编写:

引 阿修斯 自 '阿修斯';

定 网址 = `https://git.jtu.net.cn/xuexi/shuru/-/raw/master/${编码地址('书')}.json`;

途 务 读取数据() {
  试 {
    定 应答 = 等 阿修斯.取(网址);
    若(应答.状态 != 200) {
      抛 启 错('读取数据出错:' + 应答.状态);
    }
    控制台.日志(象谱.串(应答.数据));
  } 接 (错误) {
    控制台.日志(错误);
  }
}

读取数据();

缺陷

  • 您不能在非函数内或代码的顶级上下文环境中使用运算符。这有时会导致需要创建额外的函数封包,这在某些情况下会略微令人沮丧。但大部分时间都值得。