menu 光风霁月。
JS 循环-异步
448 浏览 | 2020-05-21 | 阅读时间: 约 2 分钟 | 分类: Javascript 原理 | 标签: Javascript,异步
请注意,本文编写于 242 天前,最后修改于 241 天前,其中某些信息可能已经过时。

前言

最近在使用 AWS 的 Lambda 时,由于我是用 JS 写的后端,涉及到循环访问数据库的操作,而访问数据库的方法(AWS提供)必定是异步方法,后一个循环又依赖于前一个循环的结果,必须要求同步。所以经过一些列的摸索,做出如下总结:

let 与 var

let 关键字是 ES6 版本引出来的, 与 var 在很多方面有诸多不同, 具体细节就不再多说, 只是谈一谈在循环这里的有关异步的一些知识。

for (var i = 0; i < 5; i += 1) {
    setTimeout(() => {
        console.log(i);
    }, 100);
}
// result => 5 5 5 5 5

for (let i = 0; i < 5; i += 1) {
    setTimeout(() => {
        console.log(i);
    }, 100);
}
// result => 0 1 2 3 4

这是一个十分常见的面试题, 知识点就是在循环这里 var 的作用域是全局范围的, 而且存在变量提升的效果, 解释器在解释执行时是这样的:

var i;
for(i = 0; i < 5; i += 1) {
    // other code
}

因此所有循环都在共享一个 i , 遍历结束后 i 早变为了 5, 而对于 let 关键字而言, 在每次的遍历过程中,i 都有一个新值,并且每个值都在循环内的作用域中。

循环异步(forforEach)

在实际的开发中, 总会遇到各式各样的耗时 操作, 比如文件的读取, 通过 HTTP 来加载js 文件, css 样式表。 JS 对此的工作方式却是我们不习惯也不喜欢的异步操作。即先去完成不耗时的过程, 把这种耗时的过程放入队列中等待有空时再去完成它, 这样做便不会堵塞线程(JS的工作方式为单线程), 也不会耽误其他工作。

这样的初衷是好的, 也是必要的(这里的必要指的是非要不可!)。但是异步往往也会带来许多麻烦, 如果不能掌控它, 它便不能按照我们所想的方式去工作, 那将会十分糟糕啊!

let arr = ["nice", "good", "cup"];
let time = [3000, 2000, 1000];

function asyncFunc(word, time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve(word + time) }, time);
    });
}

for(let i = 0; i < 3; i += 1) {
    asyncFunc(arr[i], time[i])
        .then(result => { console.log(result) });
        .catch(error => { console.log(error) });
}
// 结果为: cup1000 good2000 nice3000
// 预计为: nice3000 good2000 cup1000

预计与实际结果产生了偏差, 这是因为for 循环是异步执行的, 没有按照顺序执行, 这并不是我们想要的结果, 特别是每次循环都依赖上次的结果时, 这将会产生致命的错误。解决办法如下:

let promises = [];
for(let i = 0; i < 3; i += 1) {
    promises.push(asyncFunc(arr[i], time[i]));
}
Promise.all(promises).then(result => { console.log(result) })
    .catch( error => {console.log(error) });
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!