JavaScript中是否有类似于"await"的函数?

4

我有一个函数,它会在等待 sync 后加载内容。下面的函数在 Firefox 中可以完美运行,但在 IE11 中无法正常工作。

//Working in other browser and inserting the multiple records but not in IE

async function setup()
{
    await Word.run(async(context)=> {

        for (var i=0; i < 5; i++)
        {
            var controler = context.document.contentControls.getByTag("myTag"+i);
            controler.load();
            await context.sync();
            controler.items[0].insertPargraph("Adding paragraph "+i);
        }
    }
    )};

}

对于IE11,以下函数可完美地插入一个记录。
//Working in IE for the only one record
function setUp()
{
    Word.run(function (context){

        var selectedTag = context.document.contentControls.getByTag("myTag");
        context.load(selectedTag,'text');
        return context.sync().then(function()
        {
            controler.items[0].insertPargraph("Adding paragraph 0")
        });

    })
}

现在的问题是,我想要迭代循环内容,但我将返回函数写在了for循环内部,这就是它不起作用的原因。
//Below function is not working
function setUp()
{
    Word.run(function (context){

        for (var i=0; i < 5; i++)
        {
            var selectedTag = context.document.contentControls.getByTag("myTag");
            context.load(selectedTag,'text');
            return context.sync().then(function()
            {
                controler.items[0].insertPargraph("Adding paragraph 0")
            });
        }
    })
}

如何为IE11浏览器编写await函数。我已经尝试了goto Lable函数,但还是无法正常工作。


1
看起来你在第三个例子中忘记了 getByTag("myTag"+i);insertPargraph("Adding paragraph "+i)。注意 +i 部分。 - Moti Korets
2个回答

2
您的async版本在使用getTag和添加段落时使用了i,但您后续的代码示例没有使用,这对解决方案很重要。

共同点

您可以创建一个Promise链,类似于我在这里的答案,但不同之处可能很难应用到您的情况。基本上,您从已解决的Promise(p)开始,然后使用p = p.then(...)来构建链。

如果您不需要使用i的值

...那么您可以这样做:

function setUp()
{
    Word.run(function (context){
        var p = Promise.resolve();
        for (var i = 0; i < 5; i++)
        {
            p = p.then(function() {
                var selectedTag = context.document.contentControls.getByTag("myTag");
                context.load(selectedTag,'text');
                return context.sync().then(function()
                {
                    controler.items[0].insertPargraph("Adding paragraph 0")
                });
            });
        }
    })
}

如果你确实需要使用 i 的值

...那么我们需要将其编写到代码中,因为你必须使用 var(IE11 有 let,但它没有 ES2015 对于 for 循环的语义):

function setUp()
{
    Word.run(function (context){
        function doOne(index) {
            // We use `index` below
            var selectedTag = context.document.contentControls.getByTag("myTag" + index);
            context.load(selectedTag,'text');
            return context.sync().then(function()
            {
                controler.items[0].insertPargraph("Adding paragraph " + index)
            });
        }
        var p = Promise.resolve();
        for (var i = 0; i < 5; i++)
        {
            p = p.then(doOne.bind(null, i));
        }
    })
}

setUp 添加返回值

你的 async 版本假设 Word.run 返回一个 promise,并且期望它的回调函数返回一个 promise。我找不到任何支持这一点的文档,但是网页上有关这方面的文档似乎真的非常糟糕。

如果这两个假设都是正确的,那么为了让 setUp 返回一个 promise,我们只需要进行小的修改:在 Word.run 前添加 return,并在回调函数结尾处添加 return p;(见 *** 注释)。

function setUp()
{
    return Word.run(function (context){                      // ***
        function doOne(index) {
            // We use `index` below
            var selectedTag = context.document.contentControls.getByTag("myTag" + index);
            context.load(selectedTag,'text');
            return context.sync().then(function()
            {
                controler.items[0].insertPargraph("Adding paragraph " + index)
            });
        }
        var p = Promise.resolve();
        for (var i = 0; i < 5; i++)
        {
            p = p.then(doOne.bind(null, i));
        }
        return p;                                            // ***
    })
}

但如果 Word.run 没有返回一个 Promise 或者不希望从它的回调函数中得到一个 Promise,那么这种方法就不可行了,我们需要自己创建:

function setUp()
{
    return new Promise(function(resolve, reject) {           // ***
        Word.run(function (context) {
            function doOne(index) {
                // We use `index` below
                var selectedTag = context.document.contentControls.getByTag("myTag" + index);
                context.load(selectedTag,'text');
                return context.sync().then(function()
                {
                    controler.items[0].insertPargraph("Adding paragraph " + index)
                });
            }
            var p = Promise.resolve();
            for (var i = 0; i < 5; i++)
            {
                p = p.then(doOne.bind(null, i));
            }
            p.then(resolve).catch(reject);                   // ***
        })
    });
}

如果 setUp 应该是一个 async 函数,那么它不应该返回一个只有在所有内部 promise 都被解决后才解决的 promise 吗?像这样:return Promise.all(innerArrayOfPromises); - Terry
@Terry:如果Word.run返回一个Promise(微软的文档太糟糕了,我无法确定它是否会返回Promise),那么async版本确实会等待它。我找不到任何迹象表明它会等待,但我会进行更新以防万一。我们不想使用Promise.all - T.J. Crowder
@T.J.Crowder 我已经尝试了你的代码,前四个循环可以正常工作,但第五个循环会抛出“ReferenceError: 'resolve'未定义”的错误。 - mkHun
@mkHun:我理解你是指你已经尝试使用最后一个示例了。恕我直言,你一定是复制代码时出现了错误,因为resolve在上面的代码中肯定是在作用域内而不是undefined - T.J. Crowder

2

我认为您想要实现的是将sync()调用链接在一起,以便Word.run中的回调函数仅在所有同步完成时解析。您可以使用Promise.all()生成一个promise,当所有提供的promise都已解析时,它会解析。

function setUp() {
  Word.run(function(context) {
    const promises = [];
    for (var i = 0; i < 5; i++) {
      var selectedTag = context.document.contentControls.getByTag("myTag");
      context.load(selectedTag, 'text');
      let p = context.sync().then(function() {
        controler.items[0].insertPargraph("Adding paragraph 0")
      });
      promises.push(p);
    }
    return Promise.all(promises);
  })
}


从技术上讲,由于promises从未被重新分配,因此您可以安全地使用const promises... ;) 对于pselectedTag也是同样的道理。 - Terry

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