【问题标题】:Using Promises with Loops and Nested Functions使用带有循环和嵌套函数的 Promise
【发布时间】:2016-12-14 15:20:29
【问题描述】:

我正在尝试将 Promise 与循环和嵌套函数一起使用,这些循环和嵌套函数位于其中一些循环中。我有一系列函数应该从 REST 调用中带回 SharePoint 列表项 - 一旦这些函数执行完毕,就会调用另一个函数,该函数使用带回的数据。

由于有多个列表,随后每个列表中都有多个列表项,我使用了一个 while 循环来进行每个 REST 调用,然后从那里将数据(列表项)放入对象中。这些对象被放置到一个数组中,这就是第二个函数用来继续的。

我无法接收来自 Promise 的响应。我正在考虑将多个承诺推送到一个数组,然后最后使用Promise.all 来查看在使用then 之前是否一切都解决了。问题是所有的承诺都保留pending,因为我没有正确返回resolve。请看下文。

    function onQuerySuccess(sender, args) {
        var itemEnumerator = items.getEnumerator();

        while (itemEnumerator.moveNext()) {
            var promise = new Promise(function (resolve, reject) {
                var item = itemEnumerator.get_current();
                item = item.get_item('URL');
                var itemUrl = item.get_description();

                getRequestItemsFromList(itemUrl);
            });

            promises.push(promise); // all promises are present, but their status is pending
        }

        console.log(promises);

        Promise.all(promises).then(function (val) {
            console.log(val);
            execFuncs(); // function to execute once all the above are done
        }).catch(function (response) {
            console.log(response);
        });
    }

因为涉及到的函数很多,所以执行顺序如下:

getRequestItemsFromList //gets url for each list
execCrossDomainRequest (on success call) // makes REST call to get list and its items
cleanData // trims data and puts it in objects

最后是我想我打电话给Promise.resolve()的地方,因为那是行的结尾。

无论如何,这都行不通。我检查了其他线程,但我试图在不使用任何库的情况下做到这一点。提前致谢。

编辑:

完整的相关代码:

var promises = [];

window.requests = [];

function getRequestLists() {
    var requestsLists = hostWeb.get_lists().getByTitle('Name');  // sharepoint list with all the request list urls.
    context.load(requestsLists);

    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View></View>');
    var items = requestsLists.getItems(camlQuery);

    context.load(items, 'Include(URL)');

    context.executeQueryAsync(onQuerySuccess, onQueryFail);

    function onQuerySuccess(sender, args) {
        var itemEnumerator = items.getEnumerator();

        while (itemEnumerator.moveNext()) {
            var promise = new Promise(function (resolve, reject) {
                var item = itemEnumerator.get_current();
                item = item.get_item('URL');
                var itemUrl = item.get_description();

                getRequestItemsFromList(itemUrl);
            });

            promises.push(promise);
        }

        console.log(promises);

        Promise.all(promises).then(function (val) {
            console.log(val);
            execFuncs(); // not shown here
        }).catch(function (response) {
            console.log(response);
        });
    }

    function onQueryFail(sender, args) {
        alert("Request to retrieve list URL items has failed. " + args.get_message());
    }
}

function getRequestItemsFromList(url) {
    var lastPos = getSubstringIndex(url, "/", 4);
    var webUrl = url.substring(0, lastPos); // truncates list url to parse out web url       

    var absListPos = getSubstringIndex(url, "AllItems.aspx", 1);
    var absListUrl = url.substring(0, absListPos); // truncates the AllItems.aspx at the end of the list url

    var relListPos = getSubstringIndex(absListUrl, "/", 3);
    var relListUrl = absListUrl.substring(relListPos, absListUrl.length); // gets the list's relative url 

    var listName = "List Name";

    console.log(webUrl);
    execCrossDomainRequest(webUrl, listName, absListUrl);
}

function execCrossDomainRequest(webUrl, listName, absListUrl) {
    var executor = new SP.RequestExecutor(appWebUrl);

    executor.executeAsync({ // to collect the list description
        url: appWebUrl + "/_api/SP.AppContextSite(@target)/web/lists/getbytitle(@name)?" +
            "@target='" + webUrl + "'&@name='" + listName + "'" +
            "&$select=Description",
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: onCallSuccess,
        error: onCallFail
    });

    function onCallSuccess(data) {
        var json = JSON.parse(data.body);
        var description = json.d.Description;

        executor.executeAsync({ // to collect the list item information
            url: appWebUrl + "/_api/SP.AppContextSite(@target)/web/lists/getbytitle(@name)/items?" +
                "@target='" + webUrl + "'&@name='" + listName + "'" +
                "&$top=500&$select=*," +
                "Assigned_x0020_To/Title" +
                "&$expand=Assigned_x0020_To/Id",
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: onItemsCallSuccess,
            error: onItemsCallFail
        });

        function onItemsCallSuccess(data) {
            var itemsJson = JSON.parse(data.body);
            var results = itemsJson.d.results;

            cleanData(results, description, absListUrl);
        }

        function onItemsCallFail(data, errorCode, errorMessage) {
            console.log("Could not make list items cross domain call. " + errorMessage);
        }
    }

    function onCallFail(data, errorCode, errorMessage) {
        console.log("Could not make list cross domain call. " + errorMessage);
    }
}

function cleanData(results, listDescription, absListUrl) {

    if (!results.length) {
        return;
    }

    for (var i = 0; i < results.length; i++) {
        var client = listDescription;
        var id = results[i].ID;
        ...

        }

        var request = new Request(client, id, path, title, status, estimated, assignedTo, priority, description, response);

        window.requests.push(request);
    }

    return Promise.resolve();
}

【问题讨论】:

  • 我们需要知道循环中的哪些函数调用是异步的,以及它们的调用约定如何工作。此外,promise 没有任何魔力可以知道异步事情何时完成。他们只有在异步操作完成时明确解决或拒绝承诺时才知道是否完成了异步操作。取而代之的是,一旦每个 Promise 都得到了适当的检测,Promise 就会为异步操作提供各种控制流和错误处理工具。请记录循环中的所有函数调用都在做什么,尤其是异步调用。
  • @jfriend00 我在涉及功能的完整部分中添加了。
  • 你为什么在cleanData里面打电话给return Promise.resolve(),那应该怎么做?
  • 你有一系列异步函数相互依赖,例如getRequestItemsFromList -> execCrossDomainRequest -> onCallSuccess -> onItemsCallSuccess -> cleanData(更不用说它们的失败回调)你需要一个异步流控制。通常我会建议协程(使用生成器的承诺),但目前您可以简单地承诺每个步骤并将其解决方案返回到下一个阶段,您将在该阶段处理下一个 asyc 调用。我猜你每个请求都会有相同的承诺火车。
  • 是的,promisfying 是通过一个 promise 构造函数来包装异步函数,例如 var p = new Promise((resolve,reject) => setTimeout(_=> resolve(42),1000)).then(doNext ).then(doTheRealNext).then(doTheUltimateNext)。您转发的文件将为您提供基础知识。当你对承诺更有信心时;如果您想“像”同步一样处理异步工作流程,请查看我最近看到的这个出色的源代码curiosity-driven.org/promises-and-generators。在同步代码的意义上,协程让您在生成器函数中运行异步工作流。

标签: javascript rest loops promise nested-loops


【解决方案1】:

在我看来你的 promises 数组没有定义:do var promises = [];在你的 while 循环之前。

【讨论】:

  • 它位于代码的不同部分。我没有在这里展示。
  • 除此之外还有很多很多其他的错误。 OP 似乎在循环中有一堆异步操作,所有这些操作都需要通过 Promise 进行探测才能适当地对事物进行排序。
  • 这是我注意到的第一件事。但是,您可以将 Promise 推送到数组中并使用 .all() 调用它们
【解决方案2】:

当你像这样使用 Promise 构造函数时:

var promise = new Promise(function (resolve, reject) {

这意味着您在此块内的某个地方调用resolve 和/或reject 来完成承诺。

但你永远不会这样做,让创建的 Promise 对象永远处于挂起状态。

Constructing a promise

【讨论】:

  • 我有,因为代码很多,所以上面没有显示,但我在cleanData 中写了Promise.resolve(),这是上面发布的另一个函数。即使做return Promise.resolve(); 也不会改变状态。是不是因为我过早地将 promise 推入数组?
  • @LaLaLottie 你不能在那里使用Promise.resolve()Promise.reject(),这些是静态方法,而你有一个动态的Promise,所以你应该调用定义的resolve(data) 和/或reject(reason)。请参阅我提供的链接。
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 2021-07-01
  • 2018-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多