Angular 2中与$timeout等效的方法是什么?

32

在 Angular 2 环境中,我必须使用(大量的)现有代码。那段代码广泛使用了 AngularJS 1.x 的 $timeout 服务。与代码中使用的其他各种 AngularJS 1.x 服务不同,我很难找到 $timeout 服务的 Angular 2 等效信息。

Angular 文档似乎没有提到名称中带有 timeout 的服务。文章 从 AngularJS 升级确实提到了我面对的情况:

也许您想要访问 AngularJS 的内置服务,如 $location$timeout

不幸的是,该文章并没有真正解释如何访问这些特殊的服务,因为随后的示例 HeroesService 假定了一个由 AngularJS 1.x 提供的没有任何依赖项的服务。

诸如这样的文章表明,使用原生的 setTimeout函数无法满足 $timeout 服务的能力。

我该如何在 Angular 2 环境中复制 $timeout 的功能?

编辑:正如答案中所指出的那样,使用本机的 setTimeout 函数的缺点在使用 Angular 2 时并不相关。在这种情况下,如果我有完整的来自 AngularJS 1.x 的 $q,我可以大致复制 $timeout 函数,就像这样:

function $timeout(fn, delay) {
    var result = $q.defer();
    setTimeout(function () {
        $q.when(fn()).then(function (v) {
            result.resolve(v);
        });
    }, delay);
    return result.promise;
}

1
http://blog.neilni.com/2016/03/27/settimeout-in-angular-2/ - user8317956
1
https://dev59.com/LKHia4cB1Zd3GeqPbOwa#44096451 - user8317956
@slacker:delay 操作符似乎返回一个 Observable,所以我需要摆脱它,将事情恢复到适当的 Promise。第一个链接指向计时器,看起来很有前途;也许我可以根据那个创建一个类似于原始 $timeout 的包装器。我只是想知道,为什么在 Angular 文档 中找不到 timer - O. R. Mapper
Mapper - 知道这是4年前的内容,但上面代码中的'delay'是一个参数(毫秒数),而不是一个运算符。 - jessewolfe
@jessewolfe:当然,不知道我在想什么。也许我在想“操作数”,这个词更接近一点?谢谢你指出来,以免其他读者感到困惑。 - O. R. Mapper
1个回答

21
使用原生的setTimeout函数即可,不再需要在Angular中使用特殊服务。这是由于zones的引入,具体来说是NgZone
像这样的文章建议使用原生的setTimeout函数,但也有人认为$timeout服务的功能更强大。
为什么会这样说呢?$timeout服务的主要任务是在延迟函数执行后启动digest。您可以从源代码中看到这一点。
function $TimeoutProvider() {
  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
    function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {

        timeoutId = $browser.defer(function() {
          try {
            deferred.resolve(fn.apply(null, args));
          } catch (e) {
          ...

          if (!skipApply) $rootScope.$apply();  <-------------------- here
        }, delay);

在Angular中,zone.js拦截所有异步操作并启动Angular中的变更检测,这是一种增强版的digest。
如果你需要复制$timeout,你可以大致按照以下方式执行:
function $timeout(fn, delay, ...args) {
  let timeoutId;

  $timeout.cancel = $timeout.cancel || function (promise) {
    if (promise && promise.$$timeoutId in $timeout.promises) {
      $timeout.promises[promise.$$timeoutId][1]('canceled');
      delete $timeout.promises[promise.$$timeoutId];
      return clearTimeout(promise.$$timeoutId);
    }
    return false;
  };

  $timeout.promises = $timeout.promises || {};

  const promise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(function () {
      try {
        resolve(fn.apply(null, args));
      } catch (e) {
        reject(e);
      } finally {
        delete $timeout.promises[promise.$$timeoutId];
      }
    }, delay);

    $timeout.promises[timeoutId] = [resolve, reject];
  });

  promise.$$timeoutId = timeoutId;

  return promise;
}

// some basic testing

$timeout((v) => {
  console.log('a', v);
}, 2000, 7);

const promise = $timeout(() => {
  console.log('b');
}, 3000);

promise.catch((reason) => {
  console.log(reason);
});

$timeout.cancel(promise);

“为什么这么说?”- 好吧,这篇文章提到了一些需要被Angular消化的超时后操作的注意事项。 - O. R. Mapper
2
@O.R.Mapper,你提到的这篇文章讨论了AngularJS上下文的差异。它所说的并不适用于Angular。Angular使用zone.js来拦截异步操作并运行脏检查。 - Max Koretskyi
1
我已经更新了我的问题,包括一个示例来重新创建$timeout功能。 - O. R. Mapper
1
@O.R.Mapper,我已经在我的答案中添加了实现。 - Max Koretskyi
谢谢,我会接受你的答案,因为它已经很有帮助了。这将会有些复杂,因为我还想复制$timeout.cancel函数(因此必须附加一些标记到结果承诺中,以识别应该被取消的超时),但总的来说,这已经让我走上了正确的轨道。 - O. R. Mapper
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接