for...of的妙用---------forEach、map、for of 的一些本质区别

forEach、for ... of、map循环一些区别

1. 循环机制

forEach的循环是基于传统的 for 循环机制实现的,遍历数组中的每个元素的过程是同步操作

for...of的循环的本质是一个迭代循环, for...of 循环使用的是迭代器协议(Iterator Protocol),在遍历过程中是同步的 这个同步和同步操作不是一个意思,后面会讲到 ,每次调用迭代器的 next() 方法

map 方法是 JavaScript 数组的一个高阶函数,它接受一个回调函数作为参数,并对数组中的每个元素依次执行该回调函数。这个循环过程也是同步的,整个过程会按顺序执行,如果同步操作中包含异步 map() 方法会等待每个异步操作完成后再进行下一步。

2. 可执行的操作

- forEach循环请求异步的时候

前面提到了forEach的循环机制是for循环,所以他每次循环是不会等待异步请求完成了再进行下一次循环的。这就叫同步操作

javascript 复制代码
const arr = [1, 2, 3];
arr.forEach(async (item) => { 
        await new Promise(resolve => setTimeout(resolve, 1000));
        console.log(item); 
}); 
console.log('done');
//输出结果
done 1 2 3

在这个例子中,我们使用了 async/await 来让异步操作顺序执行。在回调函数中,我们使用了 await 关键字等待 1 秒钟后输出当前元素。

然而,如果你运行这段代码,你会发现你使用了 await 关键字来等待异步操作完成并没有生效,因为 await 无法直接用于 forEach 方法上,forEach 方法不支持返回 Promise 对象,它始终返回 undefined,也就是说,forEach 方法本身会继续执行下一次循环,而不会等待异步操作的完成,

javascript 复制代码
下面则是基于上面的解决办法,假设需要用forEach进行循环请求
const arr = [1, 2, 3];
let promises = [];

arr.forEach(async (item) => { 
//this.$get()是我自己封装的请求方法,返回的是一个包含请求结果的promise对象
       promises.push(this.$get('https://接口地址..?id='+item))
}); 

let results = await Promise.all(promises); 

results.forEach((res, index) => { 
    rows[index].arr = res.rows;
});
console.log(rows);
// [{ id: 1, arr: [...] }, { id: 2, arr: [...] }, { id: 3, arr: [...] }]

在本例中,我们将所有的异步操作推入 promises 数组中,并使用 Promise.all 方法等待它们的完成。一旦所有异步操作都完成了,我们就可以使用 results 数组中的结果来更新 rows 数组。

在这里forEach就和for循环没任何区别了,forEach像是一个for循环的语法糖

当然或许有比我更好的办法,我这可能有点麻烦。

- for...of循环请求异步的时候

ini 复制代码
let rows=[1,2,3]

for (let i of rows) {
//this.$get()是我自己封装的请求方法,返回的是一个包含请求结果的promise对象
	let res = await this.$get('请求接口地址' + i);
	i.arr = res.rows;
}

for...of 循环中,使用迭代器协议遍历数组的每个元素,循环体内部的代码是同步执行的,但是可以使用 await 关键字等待异步操作的完成,来保证异步操作按照正确的顺序执行。

在循环体内部使用 await 关键字等待异步操作完成时,for...of 循环本身会被阻塞,直到异步操作完成后才会继续执行下一次循环。 这就是关键所在。

for...of 循环中使用 await 关键字时,JavaScript 引擎会暂停当前的执行上下文,并将控制权交还给调用栈,直到这个 Promise 被 resolve 或 reject。一旦 Promise 被 resolve,程序将恢复执行,并且 for...of 循环将继续进行下一次迭代。

- 用map方法进行异步请求的时候

javascript 复制代码
const array = [1, 2, 3];
const promises = array.map(async (i) => { 
//this.$get()是我自己封装的请求方法,返回的是一个包含请求结果的promise对象
const {data} = await this.$get('接口地址?type=' + i);
    console.log(data) //输入出的是请求结果的信息
    return data;
}); 
Promise.all(promises) .then((newArray) => { 
// 输出异步操作结果的新数组
    console.log(newArray);
// 错误处理
}) .catch((error) => { 
    console.error(error); 
});

这里你会有疑惑吧,data,而返回值却是primise对象

这是因为map 方法是一种并行执行的方式,它会同时启动所有的异步请求。这保证了异步操作按顺序完成,并且返回的新数组与原始数组保持一致的顺序。

map 方法只是会等待这些 Promise 对象完成后再返回结果,是不能在里面处理异步返回的数据的

总结

当使用forEach方法进行循环遍历时,它是一个同步操作,循环内包含异步操作,是不会等待异步返回的结果。

解决这个问题,你可以使用 for...of 循环来遍历数组,并在每次迭代中使用 await 关键字暂停迭代过程,等待异步操作的完成

map() 方法中执行异步操作时,它并不会像 forEach() 方法那样继续迭代到下一个元素。map() 方法会等待每个回调函数的异步操作,例如Promise 对象解析完成,并将解析后的值存储在新数组中的相应位置。这保证了异步操作按顺序完成,并且返回的新数组与原始数组保持一致的顺序。

知识拓展

await 关键字用于等待一个异步操作完成,并暂停当前函数的执行,直到异步操作返回结果。它只能在 async 函数内部使用,当遇到 await 关键字时,函数的执行会被暂停,直到异步操作返回结果。这样可以避免回调地狱和复杂的 Promise 链式调用。

迭代器(Iterator)是一种用于遍历数据集合的接口,它提供了一种统一的方式来访问集合中的每个元素,而不暴露其内部的实现细节。迭代器的核心在于它定义了一个 next() 方法,next() 方法的调用是同步的。

迭代器的迭代过程是同步的。在这个过程中,每次调用都会立即返回对应的元素值和遍历状态,不会创建新的宏任务或引入异步操作。

相关推荐
新人11yj47 分钟前
如何给网页增加滚动到顶部的功能
前端·javascript
我爱加班、、26 分钟前
element-plus表单校验失败问题
前端·javascript·vue.js·elementui·ecmascript
香香甜甜的辣椒炒肉31 分钟前
vue快速上手
前端·javascript·vue.js
用户2519162427111 小时前
Canvas之概述,画布与画笔
前端·javascript·canvas
mrsk1 小时前
JavaScript之变量的解构赋值全面解析(●'◡'●)
前端·javascript·面试
归于尽1 小时前
回调函数在Node.js中是怎么执行的?
前端·javascript·node.js
浏览器API调用工程师_Taylor1 小时前
Look my eyes 都2025年了,你还不会将重复的事情自动化?
前端·javascript·爬虫
zhaocarbon1 小时前
vue2 echarts中国地图、在地图上标注经纬度及标注点
前端·javascript·echarts
咔咔咔索菲斯2 小时前
Vue 中mounted 生命周期钩子的执行时机和 v-for 的渲染顺序
前端·javascript·vue.js
前端小咸鱼一条2 小时前
Vue响应式原理一:认识响应式逻辑
前端·javascript·vue.js