先看一个例子
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
const getNumFruit = fruit => {
return fruitBasket[fruit];
}
console.log(getNumFruit('pear')); // 14
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const fn = async () => {
console.log('start');
const numApples = await getNumFruit('apple'); // 每间隔一秒输出
console.log(numApples);
const numBananas = await getNumFruit('banana'); // 每间隔一秒输出
console.log(numBananas);
const numPears = await getNumFruit('pear'); // 每间隔一秒输出对应水果的数量
console.log(numPears);
console.log('end');
}
fn();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在for循环中使用await
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const forLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
// 循环依次获取对应水果的数量
for (let i = 0; i < fruits.length; i++) {
const fruit = fruits[i];
// getNumFruit返回一个promise,使用await来等待结果的返回并打印
const fruitNum = await getNumFruit(fruit);
console.log(`${fruit}: ${fruitNum}`);
}
console.log('end');
}
forLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
当使用await时,希望JavaScript暂停执行,直到promise返回处理结果,这意味着for循环中的await应该是按顺序执行的。 结果如下:
start
apple: 27
banana: 12
pear: 14
end
1
2
3
4
5
2
3
4
5
在forEach循环中使用await
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const forEachLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
fruits.forEach(async fruit => {
const fruitNum = await getNumFruit(fruit);
console.log(`${fruit}: ${fruitNum}`);
});
console.log('end');
}
forEachLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
结果如下:
start
end
apple: 27
banana: 12
pear: 14
1
2
3
4
5
2
3
4
5
由上述结果可知,在JavaScript中的forEach不支持promise感知,也不支持async和await,所以不能在forEach使用await。
在map中使用await
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const mapLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
const numFruits = await fruits.map(async fruit => {
const fruitNum = await getNumFruit(fruit);
return fruitNum; // 这里返回的是一个promise
});
// numFruits是一个Promise数组
console.log(numFruits);
console.log('end');
}
mapLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
start
[ Promise { <pending> },
Promise { <pending> },
Promise { <pending> } ]
end
1
2
3
4
5
2
3
4
5
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const mapLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
const numFruitsPromises = fruits.map(async fruit => {
const fruitNum = await getNumFruit(fruit);
return fruitNum; // 这里返回的是一个promise
});
// numFruits是一个Promise数组
// 使用Promise.all来处理Promise数组
const numFruits = await Promise.all(numFruitsPromises);
console.log(numFruits);
console.log('end');
}
mapLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
start
[ 27, 12, 14 ]
end
1
2
3
2
3
在filter循环中使用await
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const filterLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
const moreThan20 = await fruits.filter(async fruit => {
const fruitNum = getNumFruit(fruit);
return fruitNum > 12; // 这里return的是promise,所有数组中的所有项都通过filter。
});
console.log(moreThan20);
console.log('end');
}
filterLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
start
[ 'apple', 'banana', 'pear' ]
end
1
2
3
2
3
在filter正确使用await的三个步骤:
- 使用map返回一个promise数组;
- 使用 await 等待处理结果
- 使用 filter 对返回的结果进行处理
在reduce循环中使用await
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const reduceLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
const sum = await fruits.reduce(async (sum, fruit) => {
const fruitNum = await getNumFruit(fruit);
return sum + fruitNum; // 这里return的是promise,所以最后返回的一个拼接字符串
}, 0);
console.log(sum);
console.log('end');
}
reduceLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
start
[object Promise]14
end
1
2
3
2
3
- 在第一次遍历中,sum为0。fruitNum是27(通过getNumFruit(apple)的得到的值),0 + 27 = 27。
- 在第二次遍历中,sum是一个promise。为什么?因为异步函数总是返回promises)fruitNum是12。promise无法正常添加到对象,因此JavaScript将其转换为[object Promise]字符串。 [object Promise] + 12 是object Promise] 12。
- 在第三次遍历中,sum还是一个promise。fruitNum是14. [object Promise] + 14是[object Promise] 14。
const fruitBasket = {
apple: 27,
banana: 12,
pear: 14
};
// 使用setTimeout模拟从服务器获取数据
const sleep = time => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
const getNumFruit = fruit => {
return sleep(1000).then(() => fruitBasket[fruit]);
}
const reduceLoop = async () => {
console.log('start');
const fruits = Object.keys(fruitBasket);
const sum = await fruits.reduce(async (promisedSum, fruit) => {
const sum = await promisedSum;
const fruitNum = await getNumFruit(fruit);
return sum + fruitNum;
}, 0);
console.log(sum);
console.log('end');
}
reduceLoop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在reduce中使用wait最简单(也是最有效)的方法是:
- 使用map返回一个promise 数组
- 使用 await 等待处理结果
- 使用 reduce 对返回的结果进行处理
总结
- 如果需要循环执行await,使用for循环(或任何没有回调的循环)。
- 永远不要和forEach一起使用await,而是使用for循环(或任何没有回调的循环)。
- 不要在filter和reduce中使用await,如果需要,先用map进行处理,然后再使用filter和reduce进行处理。