如何使用 q.js 承诺处理多个异步操作 [英] how to use q.js promises to work with multiple asynchronous operations

查看:18
本文介绍了如何使用 q.js 承诺处理多个异步操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:这个问题也在 Q.js 邮件列表中通过 此处.

Note: This question is also cross-posted in Q.js mailing list over here.

我遇到了多个异步操作的情况,我接受的答案指出使用 Promises 使用诸如q.js 会更有益.

i had a situation with multiple asynchronous operations and the answer I accepted pointed out that using Promises using a library such as q.js would be more beneficial.

我确信重构我的代码以使用 Promises 但因为代码很长,我已经修剪了不相关的部分并将关键部分导出到一个单独的存储库中.

I am convinced to refactor my code to use Promises but because the code is pretty long, i have trimmed the irrelevant portions and exported the crucial parts into a separate repo.

repo 是这里,最重要的文件是this.

The repo is here and the most important file is this.

要求是我希望 pageSizes 在遍历所有拖放的文件后不为空.

The requirement is that I want pageSizes to be non-empty after traversing all the dragged'n dropped files.

问题是 getSizeSettingsFromPage 函数内的 FileAPI 操作导致 getSizeSettingsFromPage 异步.

The problem is that the FileAPI operations inside getSizeSettingsFromPage function causes getSizeSettingsFromPage to be async.

所以我不能像这样放置checkWhenReady();.

So I cannot place checkWhenReady(); like this.

function traverseFiles() {
  for (var i=0, l=pages.length; i<l; i++) {
    getSizeSettingsFromPage(pages[i], calculateRatio);   
  }
  checkWhenReady(); // this always returns 0.
}

这可行,但并不理想.我更喜欢在所有 pages 成功执行此函数后调用 checkWhenReady 一次.

This works, but it is not ideal. I prefer to call checkWhenReady just ONCE after all the pages have undergone this function calculateRatio successfully.

function calculateRatio(width, height, filename) {
  // .... code 
  pageSizes.add(filename, object);
  checkWhenReady(); // this works but it is not ideal. I prefer to call this method AFTER all the `pages` have undergone calculateRatio
  // ..... more code...
}

如何重构代码以在 Q.js 中使用 Promises?

推荐答案

我的建议如下.关键是任何时候你想要异步做某事,你应该返回一个承诺,一旦任务完成你应该解决这个承诺.这允许函数的调用者监听要完成的任务,然后做其他事情.

My suggestions to get this working with Q.js are below. The key is that anytime you want to do something asynchronously, you should return a promise, and once the task is completed you should resolve that promise. That allows the callers of the function to listen for the task to be completed and then do something else.

和以前一样,我用 //*** 注释了我的更改.如果您有任何其他问题,请告诉我.

As before, I have commented my changes with // ***. Let me know if you have any further questions.

        function traverseFiles() {
            // *** Create an array to hold our promises
            var promises = [ ];
            for (var i=0, l=pages.length; i<l; i++) {
                // *** Store the promise returned by getSizeSettingsFromPage in a variable
                promise = getSizeSettingsFromPage(pages[i]);
                promise.then(function(values) {
                    var width = values[0],
                        height = values[1],
                        filename = values[2];
                    // *** When the promise is resolved, call calculateRatio
                    calculateRatio(width, height, filename);
                });
                // *** Add the promise returned by getSizeSettingsFromPage to the array
                promises.push(promise);
            }
            // *** Call checkWhenReady after all promises have been resolved
            Q.all(promises).then(checkWhenReady);
        }

        function getSizeSettingsFromPage(file) {
            // *** Create a Deferred
            var deferred = Q.defer();
            reader = new FileReader();
            reader.onload = function(evt) {
                var image = new Image();
                image.onload = function(evt) {
                    var width = this.width;
                    var height = this.height;
                    var filename = file.name;
                    // *** Resolve the Deferred
                    deferred.resolve([ width, height, filename ]);
                };
                image.src = evt.target.result;
            };
            reader.readAsDataURL(file);
            // *** Return a Promise
            return deferred.promise;
        }

<小时>

编辑

defer 创建一个 Deferred,它包含两部分,一个 promiseresolve 函数.promisegetSizeSettingsFromPage 返回.基本上,返回一个 promise 是一个函数说我稍后会回复你"的一种方式.一旦函数完成了它的任务(在这种情况下,一旦 image.onload 事件被触发),resolve 函数用于解析承诺.这表明任何等待任务已完成的承诺.


Edit

defer creates a Deferred, which contains two parts, a promise and the resolve function. The promise is returned by getSizeSettingsFromPage. Basically returning a promise is a way for a function to say "I'll get back to you later." Once the function has completed it's task (in this case once the image.onload event has fired) the resolve function is used to resolve the promise. That indicates to anything waiting on the promise that the task has been completed.

这是一个更简单的例子:

Here's a simpler example:

function addAsync(a, b) {
    var deferred = Q.defer();
    // Wait 2 seconds and then add a + b
    setTimeout(function() {
        deferred.resolve(a + b);
    }, 2000);
    return deferred.promise;
}

addAsync(3, 4).then(function(result) {
    console.log(result);
});
// logs 7 after 2 seconds

addAsync 函数添加两个数字,但在添加它们之前等待 2 秒.由于它是异步的,它返回一个承诺 (deferred.promse) 并在 2 秒等待后解析承诺 (deferred.resolve).then 方法可以在promise 上调用,并传递一个回调函数,在promise 被解析后执行.回调函数传入promise的解析值.

The addAsync function adds two numbers but it waits 2 seconds before adding them. Since it's asynchronous, it returns a promise (deferred.promse) and resolves the promise after the 2 second wait (deferred.resolve). The then method can be called on a promise and passed a callback function to be executed after the promise has been resolved. The callback function is passed in the resolution value of the promise.

在你的例子中,我们有一个 promise 数组,我们需要等待 all 完成后才能执行一个函数,所以我们使用了 Q.all.举个例子:

In your case, we had an array of promises and we needed to wait for all of them to be done before executing a function, so we used Q.all. Here's an example:

function addAsync(a, b) {
    var deferred = Q.defer();
    // Wait 2 seconds and then add a + b
    setTimeout(function() {
        deferred.resolve(a + b);
    }, 2000);
    return deferred.promise;
}

Q.all([
    addAsync(1, 1),
    addAsync(2, 2),
    addAsync(3, 3)
]).spread(function(result1, result2, result3) {
    console.log(result1, result2, result3);
});
// logs "2 4 6" after approximately 2 seconds

这篇关于如何使用 q.js 承诺处理多个异步操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆