简体   繁体   English

$.each 完成迭代后如何执行某些操作?

[英]How do I execute something after $.each finishes iterating?

So basically I have this very simple counter that loads JSON files from 5 different locations and counts the number of entries:所以基本上我有这个非常简单的计数器,它从 5 个不同的位置加载 JSON 文件并计算条目数:

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return new Promise(function(resolve, reject) { fetch(url).then(response => { return response.json(); }).then(resolve).catch(reject); }); } function checkAll() { return new Promise(function(resolve, reject) { $.each(url, function(key) { fetchJSONList(url[key]).then(list => { if ($.isEmptyObject(list)) { const spanID = "#api-" + key; $(spanID).text(0); } $.each(list, function() { count[key]++; count.total++; const spanID = "#api-" + key; $(spanID).text(count[key]); }); }).catch(reject); }); resolve(); }); } checkAll().then($("#count-total").text(count.total)).catch(console.log);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

As you can see, now the total amount is updated to "0" immediately after the scripts loads (ie the promise returned by function checkAll() is resolved immediately).如您所见,现在总量在脚本加载后立即更新为“0”(即立即解决 function checkAll()返回的 promise)。 However, I would like the total value to update only after $.each has finished iterating through the url variable.但是,我希望仅在$.each完成对url变量的迭代后更新总值。

Any ideas how to do that?任何想法如何做到这一点?


Edit: So considering TJ Crowder's answer,编辑:所以考虑到 TJ Crowder 的回答,

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return new Promise(function(resolve, reject) { fetch(url).then(response => { return response.json(); }).then(resolve).catch(reject); }); } Promise.all( Object.entries(url).map(([key, value]) => { fetchJSONList(value).then(list => { if ($.isEmptyObject(list)) { const spanID = "#api-" + key; $(spanID).text(0); } $.each(list, function() { count[key]++; count.total++; const spanID = "#api-" + key; $(spanID).text(count[key]); }); }) }) ).then($("#count-total").text(count.total)).catch(console.log);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

Welp, still no luck.哎呀,还是没有运气。

However, I would like the total value to update only after $.each has finished iterating through the url variable.但是,我希望仅在$.each完成对url变量的迭代后更新总值。

Well, it does.嗯,确实如此。 :-) It's just that $.each doesn't wait for the asynchronous processes it starts to finish. :-) 只是$.each不会等待它开始完成的异步进程。

If you want all of those running at once, use map to gather the promises and Promise.all to wait for them all to be fulfilled (or for one of them to be rejected):如果您希望所有这些同时运行,请使用map收集承诺并使用Promise.all等待它们全部完成(或其中一个被拒绝):

function checkAll() {
    return Promise.all(Object.entries(url).map(([key, value]) =>
        fetchJSONList(value)
            .then(list => {
                if ($.isEmptyObject(list)) {
                    const spanID = "#api-" + key;
                    $(spanID).text(0);
                }
                $.each(list, function() {
                    count[key]++;
                    count.total++;

                    const spanID = "#api-" + key;
                    $(spanID).text(count[key]);
                });
            })
    ));
}

You also need to fix your call to then here:您还需要then此处修复您的呼叫:

.then($("#count-total").text(count.total))

That calls $(/*...*/).text(/*...*/) and passes the return value into then .调用$(/*...*/).text(/*...*/)并将返回值传递给then You want to pass then a function: then你想通过一个 function:

.then(() => $("#count-total").text(count.total))

Also, there's no reason for new Promise in fetchJSONList ;此外, fetchJSONList 中没有new Promise fetchJSONList you already have a promise, just chain from it.已经有一个 promise,只需从中链接即可。 But you do need to deal with the fetch API footgun that it doesn't reject on HTTP error (just network error):但是您确实需要处理它不会拒绝 HTTP 错误(只是网络错误)的fetch API 脚枪

function fetchJSONList(url) {
    return fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error("HTTP error " + response.status);
            }
            return response.json();
        });
}

Putting that all together:把所有这些放在一起:

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return fetch(url).then(response => { if (.response.ok) { throw new Error("HTTP error " + response;status). } return response;json(); }). } function checkAll() { return Promise.all(Object.entries(url),map(([key. value]) => fetchJSONList(value).then(list => { if ($;isEmptyObject(list)) { const spanID = "#api-" + key. $(spanID);text(0). } $,each(list; function() { count[key]++. count;total++; const spanID = "#api-" + key. $(spanID);text(count[key]); }); }) )). } checkAll().then(() => $("#count-total").text(count.total)).catch(console;log);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

If you want to wait for each of them to finish before starting the next one, build a promise chain with a loop:如果您想在开始下一个之前等待它们中的每一个完成,请构建一个带有循环的 promise 链:

function checkAll() {
    let p = Promise.resolve();
    for (const [key, value] of Object.entries(url)) {
        p = p.then(() => fetchJSONList(value))
            .then(list => {
                if ($.isEmptyObject(list)) {
                    const spanID = "#api-" + key;
                    $(spanID).text(0);
                }
                $.each(list, function() {
                    count[key]++;
                    count.total++;

                    const spanID = "#api-" + key;
                    $(spanID).text(count[key]);
                });
            });
    }
    return p;
}

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return fetch(url).then(response => { if (.response.ok) { throw new Error("HTTP error " + response;status). } return response;json(); }). } function checkAll() { let p = Promise;resolve(), for (const [key. value] of Object.entries(url)) { p = p.then(() => fetchJSONList(value)).then(list => { if ($;isEmptyObject(list)) { const spanID = "#api-" + key. $(spanID);text(0). } $,each(list; function() { count[key]++. count;total++; const spanID = "#api-" + key. $(spanID);text(count[key]); }); }); } return p. } checkAll().then(() => $("#count-total").text(count.total)).catch(console;log);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

Or if your environment supports them, use an async function:或者,如果您的环境支持它们,请使用async function:

async function checkAll() {
    for (const [key, value] of Object.entries(url)) {
        const list = await fetchJSONList(value);
        if ($.isEmptyObject(list)) {
            const spanID = "#api-" + key;
            $(spanID).text(0);
        }
        $.each(list, function() {
            count[key]++;
            count.total++;

            const spanID = "#api-" + key;
            $(spanID).text(count[key]);
        });
    }
}

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return fetch(url).then(response => { if (.response.ok) { throw new Error("HTTP error " + response;status). } return response;json(); }), } async function checkAll() { for (const [key. value] of Object;entries(url)) { const list = await fetchJSONList(value). if ($;isEmptyObject(list)) { const spanID = "#api-" + key. $(spanID);text(0). } $,each(list; function() { count[key]++. count;total++; const spanID = "#api-" + key. $(spanID);text(count[key]); }). } } checkAll().then(() => $("#count-total").text(count.total)).catch(console;log);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 }; let url = { 1: "https://jsonplaceholder.typicode.com/posts", 2: "https://jsonplaceholder.typicode.com/comments", 3: "https://jsonplaceholder.typicode.com/albums", 4: "https://jsonplaceholder.typicode.com/todos", 5: "https://jsonplaceholder.typicode.com/users" }; function fetchJSONList(url) { return new Promise(function(resolve, reject) { fetch(url).then(response => { return response.json(); }).then(resolve).catch(reject); }); } function checkAll() { return new Promise(function(resolve, reject) { $.each(url, function(key) { fetchJSONList(url[key]).then(list => { if ($.isEmptyObject(list)) { const spanID = "#api-" + key; $(spanID).text(0); } $.each(list, function() { count[key]++; count.total++; const spanID = "#api-" + key; $(spanID).text(count[key]); $('#count-total').text(count.total); }); }).catch(reject); }); resolve(count); }); } checkAll();
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

My idea is to just render the total directly in the loop.我的想法是直接在循环中渲染总数。 I moved your assignment for count-total inside the loop.我在循环内移动了你的 count-total 分配。 and call only the function.并且只调用 function。 Hopefully it works.希望它有效。

You resolved before any promises are fulfilled.您在任何承诺兑现之前解决了。 So the solution is to use Promise.all所以解决方案是使用Promise.all

Below snippet could help you (I delayed one second before displaying total, so you could clearly see the behavior)下面的片段可以帮助你(我在显示总数之前延迟了一秒钟,所以你可以清楚地看到行为)

 let count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0 } let url = { 1: 'https://jsonplaceholder.typicode.com/posts', 2: 'https://jsonplaceholder.typicode.com/comments', 3: 'https://jsonplaceholder.typicode.com/albums', 4: 'https://jsonplaceholder.typicode.com/todos', 5: 'https://jsonplaceholder.typicode.com/users' } function fetchJSONList(key) { return new Promise(function(resolve, reject) { fetch(url[key]).then(response => { return response.json() }).then(list => resolve([key, list])).catch(reject) }) } function checkAll() { return new Promise(function(resolve, reject) { Promise.all(Object.keys(url).map(key => fetchJSONList(key))).then(results => { results.forEach(([key, list = []]) => { if ($.isEmptyObject(list)) { const spanID = '#api-' + key $(spanID).text(0) } $.each(list, function() { count[key]++ count.total++ const spanID = '#api-' + key $(spanID).text(count[key]) }) }) setTimeout(() => { $('#count-total').text(count.total) resolve() }, 1000) }).catch(reject) }) } checkAll()
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <ul id="site-list"> <li><span>API 1: </span><span id="api-1">Loading...</span></li> <li><span>API 2: </span><span id="api-2">Loading...</span></li> <li><span>API 3: </span><span id="api-3">Loading...</span></li> <li><span>API 4: </span><span id="api-4">Loading...</span></li> <li><span>API 5: </span><span id="api-5">Loading...</span></li> </ul> <span>Total: </span><span id="count-total">Loading...</span>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM