概述

有时候下面的代码有参数需要从上面执行的异步函数中获取,但是当异步函数没有执行完时,下面的代码就执行完了(此时参数为空),从而出错

由于JavaScript是单线程的,所以代码的执行是同步的,代码从上至下执行,这里贴一下JavaScript代码的执行顺序:(原因可以查看下面的JavaScript运行机制)

  1. 同步代码,包括promise的构造函数
  2. promise.then()中的代码
  3. setTimeout函数,关于该函数的等待时间:最小值不得小于4毫秒,如果低于这个值,则默认是4毫秒

JavaScript运行机制

同步代码(包括promise的构造函数)在执行栈中执行,同时异步任务按顺序进入异步任务队列(队列是先进先出的),然后将异步任务分配到微任务队列(nextTick,Promise.then())和任务队列(DOM,AJAX,setTimeout,setImmediate)

同步代码执行完毕后,微任务队列中的异步任务依次进入执行栈并执行,每次进入一个,执行完毕后再进入下一个,这就是Event Loop(事件循环),最后执行任务队列的异步任务

image-20230111224618305

解决办法

我们可以使用Promise保证程序的执行顺序,此时虽然问题解决了,但是Promise使用起来会有点繁琐,我们可以使用asyncawait关键词简化操作

代码实现

初始代码

f1f3都执行完毕后,f2内的代码才会执行,原因查看上面的JavaScript运行机制

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
35
const flag = ref();

const f1 = () => {
console.log('f1');
}

//通过设置flag的值影响f3
const f2 = () => {
//3秒后才在控制台输出,并将flag的值设置成true
setTimeout(() => {
console.log('f2');
flag.value = true;
}, 3000);
}

//会因为flag的值受到f2的影响
const f3 = (flag) => {
console.log('f3');
//flag为true代表程序正常
if (flag) {
console.log('函数f2将flag的值设置成了true');
} else {
console.log('函数f2没有及时将flag的值设置成true');
}
}

//执行f1,f2,f3三个函数
const init = () => {
f1();
f2();
f3(flag.value);
}

//打开页面就执行init函数
init();

控制台输入如下图所示,结果会输出函数f2没有及时将flag的值设置成true,且f2排在最后

image-20230111032315187

上面的结果并不符合我们的预期(上面的代码执行完后才执行下面的),我们的预期是输出函数f2将flag的值设置成了true,且f2排在f1f3之间,如下图所示

image-20230111040956780

使用Promise

f2这个函数的结果是返回一个Promise对象,在.then()中执行f3可以保证:f2执行完了之后才开始执行f3此时已经把问题解决了

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
35
36
37
38
39
40
41
42
43
44
const flag = ref();

const f1 = () => {
console.log('f1');
}

const f2 = () => {
return new Promise((resolve, reject) => {
//3秒后才在控制台输出,并将flag的值设置成true
setTimeout(() => {
//返回f2然后通过.then()在控制台输出
resolve('f2');
flag.value = true;
}, 3000);
});
}

const f3 = (flag) => {
console.log('f3');
//flag为true代表程序正常
if (flag) {
console.log('函数f2将flag的值设置成了true');
} else {
console.log('函数f2没有及时将flag的值设置成true');
}
}

//执行f1,f2,f3三个函数
const init = () => {
f1();
/*const promise = f2();
promise.then(res => {
console.log(res);
f3(flag.value);
})*/
//这里是上面注释代码简写,f2执行完后才会执行.then()中的代码
f2().then(res => {
console.log(res);
f3(flag.value);
});
}

//打开页面就执行init函数
init();

使用async和await简化

使用asyncawait关键词不但可以简化代码以提高代码的可读性,还可以避免回调地狱

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
35
36
37
38
const flag = ref();

const f1 = () => {
console.log('f1');
}

const f2 = () => {
return new Promise((resolve, reject) => {
//3秒后才在控制台输出,并将flag的值设置成true
setTimeout(() => {
//返回f2然后通过.then()在控制台输出
resolve('f2');
flag.value = true;
}, 3000);
});
}

const f3 = (flag) => {
console.log('f3');
//flag为true代表程序正常
if (flag) {
console.log('函数f2将flag的值设置成了true');
} else {
console.log('函数f2没有及时将flag的值设置成true');
}
}

//执行f1,f2,f3三个函数,使用了async关键词
const init = async () => {
f1();
//使用await关键词,从而阻塞下面代码的执行,保证f2执行完之后才会执行f3
const res = await f2();
console.log(res);
f3(flag.value);
}

//打开页面就执行init函数
init();

PS.

Promise中文文档:Promise - JavaScript | MDN (mozilla.org)