Are Angular's promises asynchronous?

2019-02-27 14:47发布

I may have miss something about Angular's promises but I was wondering something : are promises asynchronous ? I'm not sure if 'asynchronous' is the right word but let me explain myself.

In my code I use promises to do a really big process (read and write hundreds of big files) while I display a loading bar to watch the progress of the process. I've noticed that even if my code is in a promise, it seems to not really be asynchronous and freeze the display (that I assume is manage by the main thread).

For example in the code bellow that you can find in this Plnkr, I'm wondering how to let the progress bar move while the big process is done. I understand why it's freezing when I call it in the main thread but not when I'm using Angular's promises.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $q) {
  
  function hugeProcess () {
    var i = 0;
    var start = new Date().getTime();
    while (i++ < 100000) {
      console.log(i);
    }
    var end = new Date().getTime();
      var time = end - start;
      $scope.processTime = 'Done in ' + time  + 'ms';
  }
  
  $scope.onClickStartHugeProcess = function () {
    console.log('onClickStartHugeProcess');
    hugeProcess();
  };
  
  $scope.onClickStartHugeProcessWithPromise = function () {
    console.log('onClickStartHugeProcessWithPromise');
    $q.when()
    .then(function () {
      return hugeProcess();
    });
  };
});

3条回答
干净又极端
2楼-- · 2019-02-27 15:22

Web Worker is right solution. I had similar problem and developed angular plugin ng-vkThread to simplify such kind of tasks.

Basic usage is:

/* function to execute in a thread */
function foo(n, m){ 
    return n + m;
}

/* create an object, which you pass to vkThread as an argument*/
var param = {
   fn: foo      // <-- function to execute
   args: [1, 2] // <-- arguments for this function
};

/* run thread */
vkThread.exec(param).then(
   function (data) {
       console.log(data);  // <-- thread returns 3 
    },
    function(err) {
        alert(err);  // <-- thread returns error message
    }

);

Live demo

--Vadim

查看更多
Rolldiameter
3楼-- · 2019-02-27 15:23

The issue in your code is that your hugeProcess function never yields. So yes, it's called asynchronously (then callbacks are always called asynchronously in a Promises/A+ promise implementation), but that doesn't change what hugeProcess is doing when it gets called, which is hogging the main UI thread such that nothing else can happen while it's running. There's only one main UI thread, and all of your JavaScript runs on that one main UI thread except web workers.

To make hugeProcess not do that, you have to break it up and have it call itself after a brief delay, via setTimeout (or perhaps something built into Angular).

As Joe Clay points out, this code doesn't make a lot of sense:

$q.when()
.then(function () {
  return hugeProcess();
});

That's effectively:

setTimeout(hugeProcess, 0);

...since $q.when() with no arguments returns a resolved promise, and adding a then callback to a resolved promise just results in your callback being called as soon as possible (but asynchronously; e.g., then returns before the callback is called).

查看更多
姐就是有狂的资本
4楼-- · 2019-02-27 15:31

So, I've discover Web Workers and here is a first version of my code using them.

app.controller('MainCtrl', function($scope, $q) {

  function hugeProcess () {
    var i = 0;
    var start = new Date().getTime();
    while (i++ < 100000) {
      console.log(i);
    }
    var end = new Date().getTime();
    var time = end - start;
    postMessage(time);
  }

  var blob = new Blob(["onmessage = " + hugeProcess.toString()]);

  // Obtain a blob URL reference to our worker 'file'.
  var blobURL = window.URL.createObjectURL(blob);
  var worker = new Worker(blobURL);

  worker.onmessage = function (message) {
    $scope.processTime = 'Done in ' + message.data  + 'ms';
    $scope.$apply()
  };

  $scope.onClickStartHugeProcessWithPromise = function () {
    console.debug('onClickStartHugeProcessWithPromise');
    $q(function () {
      worker.postMessage(''); // Start the worker.
    });
  };
});

I don't think I'm using right but it does what I want ... I've found the package ng-webworker for Angular that seems to mix promises and web workers so that's exactly what I'm looking for.

Thank you all for your help.

查看更多
登录 后发表回答