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

时间:2020-07-20 09:17:02

标签: javascript jquery asynchronous es6-promise

所以基本上我有一个非常简单的计数器,可以从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>

如您所见,现在脚本加载后,总金额立即更新为“ 0”(即,函数checkAll()返回的诺言立即得到解决)。但是,我希望总值仅在$.each完成对url变量的迭代之后才更新。

有什么想法怎么做?


编辑:因此考虑T.J.人群的答案,

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>

很高兴,仍然没有运气。

3 个答案:

答案 0 :(得分:4)

但是,我希望总值仅在$.each完成对url变量的迭代之后才更新。

是的。 :-)只是$.each不等待异步过程开始完成。

如果您希望所有这些同时运行,请使用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]);
                });
            })
    ));
}

您还需要在此处将呼叫固定为then

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

调用 $(/*...*/).text(/*...*/),并将返回值传递到then中。您要传递then一个函数:

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

此外,new Promise中没有理由使用fetchJSONList;您已经已经有了一个诺言,只需兑现诺言。但是您确实需要处理fetch API footgun,它不会因HTTP错误(只是网络错误)而拒绝:

function fetchJSONList(url) {
    return fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error("HTTP error " + response.status);
            }
            return response.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 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>

如果要在开始下一个之前等待它们中的每个完成,请构建带有循环的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>

或者如果您的环境支持它们,请使用async函数:

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>

答案 1 :(得分:0)

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>

我的想法是直接在循环中渲染总计。我在循环内将您的分配总数转移了。并仅调用该函数。希望它能工作。

答案 2 :(得分:0)

您在任何诺言兑现之前就下定决心。因此解决方案是使用Promise.all

下面的代码片段可以为您提供帮助(我在显示总计之前延迟了一秒钟,因此您可以清楚地看到行为)

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>