javascript - 循环中的异步回调

标签 javascript asynchronous promise ms-office office-js

我有一个变量oldBindings,它记录了 Excel 表的所有现有绑定(bind)。我基于 oldBindings 构建了 BindingDataChanged 监听器。因此,当 newBindings 出现时,我需要删除链接到 oldBindings 的所有旧监听器,并添加基于 newBindings 的新监听器。目前,我编写了以下代码:

var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions

function updateEventHandlers(newBindings) {
    removeEventHandlers(oldBindings, function () {
        addEventHandlers(newBindings)
    })
}

function removeEventHandlers(oldBindings, cb) {
    for (var i = 0; i < oldBindings.length; i++) {
        Office.select("binding#"+oldBindings[i]).removeHandlerAsync(Office.EventType.BindingDataChanged, function (asyncResult) {
            Office.context.document.bindings.releaseByIdAsync(oldBindings[i], function () {});
        });
    }
    cb()
}

由于 removeHandlerAsyncreleaseByIdAsync 是使用 callback 而不是 promise 构建的,我需要组织整个代码与回调。有两件事我不确定:

1) 在removeEventHandlers 中,cb() 是否总是在删除所有监听器后执行?我怎样才能确保这一点?

2) 我是否必须将 addEventHandlers 作为 removeEventHandlers回调以确保其执行顺序?

最佳答案

1) in removeEventHandlers, will cb() ALWAYS be executed after the removal of all the listeners?

没有。它将在删除启动后被调用。但如果删除是异步的,则可能会在删除完成之前调用它。

2) Do I have to make addEventHandlers as a callback of removeEventHandlers to ensure their execution order?

是的,但不是你的方式。你所拥有的方式就像做

removeEventHandlers();
addEventHandlers();

因为您在 removeEventHandlers 末尾调用 cb,而无需等待任何操作完成。

As removeHandlerAsync and releaseByIdAsync are built with callback rather than promise, I need to organise the whole code with callback.

或者你可以给自己提供它们的 Promise 版本。稍后会详细介绍。

使用非 Promise 回调方法,以确保在所有工作完成后从 removeEventHandlers 调用 cb,请记住您期望的回调次数并等待在调用 cb 之前你会得到这么多:

var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions

function updateEventHandlers(newBindings) {
    removeEventHandlers(oldBindings, function() {
        addEventHandlers(newBindings);
    });
}

function removeEventHandlers(oldBindings, cb) {
    var waitingFor = oldBindings.length;
    for (var i = 0; i < oldBindings.length; i++) {
        Office.select("binding#"+oldBindings[i]).removeHandlerAsync(Office.EventType.BindingDataChanged, function (asyncResult) {
            Office.context.document.bindings.releaseByIdAsync(oldBindings[i], function () {
                if (--waitingFor == 0) {
                    cb();
                }
            });
        });
    }
}

但是只要你有回调系统,你就可以 Promise 化它:

function removeHandlerPromise(obj, eventType) {
    return new Promise(function(resolve, reject) {
        obj.removeHandlerAsync(eventType, function(asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Failed) {
                reject(asyncResult.error);
            } else {
                resolve(asyncResult.value);
            }
        });
    });
}

function releaseByIdPromise(obj, value) {
    return new Promise(function(resolve, reject) {
        obj.releaseByIdAsync(value, function(asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Failed) {
                reject(asyncResult.error);
            } else {
                resolve(asyncResult.value);
            }
        });
    });
}

然后你就可以这样做:

var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions

function updateEventHandlers(newBindings) {
    removeEventHandlers(oldBindings).then(function() {
        addEventHandlers(newBindings);
    });
}

function removeEventHandlers(oldBindings) {
    return Promise.all(oldBindings.map(function(binding) {
        return removeHandlerPromise(Office.select("binding#"+binding), Office.EventType.BindingDataChanged).then(function() {
            return releaseByIdPromise(Office.context.document.bindings, binding);
        });
    });
}

或者您可以为自己提供一个通用的 Promise-ifier,用于返回 AsyncResult 的任何异步操作:

function promisify(obj, method) {
    var args = Array.prototype.slice.call(arguments, 2);
    return new Promise(function(resolve, reject) {
        args.push(function(asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Failed) {
                reject(asyncResult.error);
            } else {
                resolve(asyncResult.value);
            }
        });
        obj[method].apply(obj, args);
    });
}

然后你就可以这样做:

var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions

function updateEventHandlers(newBindings) {
    removeEventHandlers(oldBindings).then(function() {
        addEventHandlers(newBindings);
    });
}

function removeEventHandlers(oldBindings) {
    return Promise.all(oldBindings.map(function(binding) {
        return promisify(Office.select("binding#"+binding), "removeHandlerAsync", Office.EventType.BindingDataChanged).then(function() {
            return promisify(Office.context.document.bindings, "releaseByIdAsync", binding);
        });
    });
}

关于javascript - 循环中的异步回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42848775/

相关文章:

java - 通过 Java NIO 非阻塞 SocketChannel 写入和读取对象

java - 等待 javaStream 上的 forEach 结束

javascript - 用于抓取的 headless chrome Nodejs 异步循环

javascript - onEdit 触发谷歌脚本不工作

javascript - 注入(inject)了恶意的 JavaScript 代码 - 但没有做任何恶意的事情?

javascript - nodejs 通过异步调用遍历数组?

javascript - 从 Promise 调用递归函数

javascript - 使用 Jest 测试 React 组件的方法

node.js - 在模块加载和配置期间使用 Nodejs Promises (Bluebird)

javascript - 网络抓取和 promise