将 Deferred 添加到非异步函数

Adding a Deferred to a non async function

我目前正在开发一个需要很长时间才能完成的函数,因为我无法让它更快地完成,我打算从其他脚本中调用它,我想知道是否有在该函数中使用类似 promise 的方法。

基本上

function longrunning(){
   var def = new $.Deferred();
   var result = {};
   [DO STUFF THAT TAKES A WHILE]
   def.resolve();
   return def.promise(result);
}

我的基本问题是,由于所有正在进行的事情都不是异步的,所以我的承诺在所有事情都完成之前不会 returned,所以稍后将调用 longrunning 的函数不会'不知道它是异步的。但是当然,如​​果我 return 在执行所有代码之前承诺,它根本不会解决。我希望你得到我正在努力做的事情。希望有人有想法。在此先感谢

你好,克里斯

将代码包装在 $.Deferred(或本机承诺)中将无济于事,即使您在执行 long-running 工作之前设法将承诺返回给调用代码(例如, 通过 setTimeout).它所能完成的就是让主 UI 线程在 之后 ,在 longrunning return 承诺后不久,而不是在调用 longrunning 本身。所以,没有用。 :-)

如果有问题的函数不操纵 DOM,或者如果它所做的操纵可以从 long-running 逻辑中分离出来,那么这是一个很好的候选者,可以移动到 web worker (specification, MDN),所以它根本不会在主 UI 线程上获得 运行,而是获得 运行在并行工作线程中,让 UI 自由地继续响应。

longrunning 不会做实际的工作,它只会 postMessage 工作人员要求它做工作,然后在它返回一条工作消息时解决你的承诺已经完成了。沿着这些方向的东西(这只是一个代码草图,不是 fully-implemented 解决方案):

var pendingWork = {};
var workId = 0;
var worker = new Worker("path/to/web/worker.js");
worker.addEventListener("message", function(e) {
    // Worker has finished some work, resolve the Deferred
    var d = pendingWork[e.data.id];
    if (!d) {
        console.error("Got result for work ID " + e.data.id + " but no pending entry for it was found.");
    } else {
        if (e.data.success) {
            d.resolve(e.data.result);
        } else {
            d.reject(e.data.error);
        }
        delete pendingWork[e.data.id];
    }
});

function longrunning(info) {
    // Get an identifier for this work
    var id = ++workid;
    var d = pendingWork[id] = $.Deferred();
    worker.postMessage({id: id, info: info});
    return d.promise();
}

(假定工作人员发回的是一个具有属性 id [工作 ID]、success [标志] 和 result [结果] 的对象或 error [错误]。)

如您所见,我们 longrunning 将工作发送给工作人员,并 return 对它的承诺;当工作人员发回工作时,侦听器会解析 Deferred。

如果 long-running 任务 确实 需要进行 DOM 操作作为其工作的一部分,它可以 post 返回必要的信息到主脚本,让它在必要时代表它进行这些操作。它的可行性自然取决于代码在做什么。


当然,如果您只需要在 up-to-date 浏览器上 运行(或包含一个 polyfill),您可以使用本机承诺而不是 jQuery 的 $.Deferred :

var pendingWork = {};
var workId = 0;
var worker = new Worker("path/to/web/worker.js");
worker.addEventListener("message", function(e) {
    // Worker has finished some work, resolve the Deferred
    var work = pendingWork[e.data.id];
    if (!work) {
        console.error("Got result for work ID " + e.data.id + " but no pending entry for it was found.");
    } else {
        if (e.data.success) {
            work.resolve(e.data.result);
        } else {
            work.reject(e.data.error);
        }
        delete pendingWork[e.data.id];
    }
});

function longrunning(info) {
    return new Promise(function(resolve, reject) {
        // Get an identifier for this work
        var id = ++workid;
        pendingWork[id] = {resolve: resolve, reject: reject};
        worker.postMessage({id: id, info: info});
    });
}