前端面试之异步任务顺序化(async/await)

1,284 阅读3分钟

1.引言

前端面试关于异步实现的问题有很多,中心相近,侧重不一,难度参差。细节颇多。其中一类为它的顺序化,例:考题场景为:在常用的循环中用实现await可能产生的问题,如何选择循环,理由是

async function test() {
	let arr = [6, 5, 8]
	/*
        设计循环输出arr[6,5,8]
        */
        
        console.log('结束')
}


function handle(x) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve(x)
		}, 2000 * x)
	})
}

test()

2.测试

1.错误答案forEach()。使用它并不能使输出按照数组元素顺序输出,数字的顺序依据Promise的setTimeOut设置的时间升序.有时候程序的多个任务执行需要遵循一定顺序,但任务花费的时间不同。这样的处理会产生问题。

arr.forEach(async item => {
		const res = await handle(item)
		console.log(res)
	})

输出结果如下

image.png 2.问题的原因:在forEach的底层实现中可以找到问题。forEach 拿过来直接执行了,这就导致它无法保证异步任务的执行顺序。由于循环执行一遍的时间远远少于Promise中耗时任务的执行时间,耗时少的数字下标先被判断,先执行回调函数,先输出

for (var i = 0; i < length; i++) {
  if (i in array) {
    var element = array[i];
    callback(element, i, array);//回调函数接受三个参数,当前的值,下标,数组名来执行
  }
}

3.正确答案for of 。输出如我们所愿

image.png

async function test() {
  let arr = [6, 5, 8]
  for(const item of arr) {
		const res = await handle(item)
		console.log(res)
  }
	console.log('结束')
}

4.成功的原因:Iterator(迭代器),for of 采用迭代器遍历数组,原生具有[Symbol.iterator]属性数据类型为可迭代数据类型。如数组、类数组(如arguments、NodeList)、Set和Map。如码可知,Symbol.iterator为数组每项添加了额外属性,value和done;后者是保证成功的核心。通过判断done(是否完成),配合迭代器的next()方法决定是否执行数组的下一任务。

let arr = [6, 5, 8];
// 这就是迭代器
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

// {value: 4, done: false}
// {value: 2, done: false}
// {value: 1, done: false}
// {value: undefined, done: true}

所以for of相当于以下代码

async function test() {
  let arr = [6, 5, 8]
  let iterator = arr[Symbol.iterator]();
  let res = iterator.next();
  while(!res.done) {//循环的条件是当前任务未完成
    let value = res.value;
    console.log(value);
    await handle(value);
    res = iterater.next();//当值被await handle(处理)时,才开启下个任务,保证顺序
  }
	console.log('结束')
}
  1. for in for in遍历数组的毛病 1.index索引为字符串型数字,不能直接进行几何运算 2.遍历顺序有可能不是按照实际数组的内部顺序 3.使用for in会遍历数组所有的可枚举属性,包括原型。 所以for in更适合遍历对象,不要使用for in遍历数组。但它也可以达到目的。只需稍稍改变。原理和for of 相似。
for(const item in arr) {         
    const res = await handle(arr[item])     
    console.log(res)   
  }

3.总结

这个问题更多是在考查我们对各种循环的底层实现的理解,不过关键在于异步任务的耗时大小和正确执行顺序的矛盾。在面试中我们先需要把握矛盾,沿着正确的思路出发。

本人大三,正寻实习,与君共勉。寥有拙作,万望指正