JavaScript异步回调与Promise的变量引用问题咨询
JavaScript异步执行与变量引用问题解析
问题场景
目标是让代码输出 1 3 2,以下是测试的几段代码:
第一段代码(无额外传参)
let data = 1; console.log(data); data = 2; setTimeout((data) => { console.log(data); }, 2000); data = 3; console.log(data);
这段代码会输出 1 3 undefined,因为setTimeout回调的data参数没有被传入值。
第二段代码(setTimeout传参)
let data = 1; console.log(data); data = 2; setTimeout((data) => { console.log(data); }, 2000, data); data = 3; console.log(data);
这段代码能得到预期的1 3 2输出。
第三段代码(回调无参数)
let data = 1; console.log(data); data = 2; setTimeout(() => { // argument removed from callback function console.log(data); }, 2000, data); data = 3; console.log(data);
这段代码却输出1 3 3,同时用Promise封装的代码也得到相同结果:
let data=1; console.log(data); data=2; temp=new Promise((resolve, reject)=>{ // async code setTimeout(()=>{ resolve(data); },2000); }) temp.then((temp)=>{ console.log(temp); }).catch((err)=>{console.log(err);} ); data=3; console.log(data);
核心疑问:
- 第三段代码中,为什么回调函数未使用setTimeout中传入的data参数?
- 为什么Promise封装的代码也输出
1 3 3? - JavaScript函数的参数对其回调函数是否不可见?
问题解答
1. 第三段代码未使用setTimeout传参的原因
setTimeout的第三个及以后的参数,是用来传递给回调函数的实参,但有个前提:回调函数必须定义对应的形参来接收。
- 第二段代码中,回调定义了
(data)形参,setTimeout传入的data=2会被赋值给这个局部形参,此时回调里的data和外部全局的data是两个独立变量,所以哪怕后续外部data改成3,回调执行时还是用的传入的2,输出2。 - 第三段代码中,回调没有定义形参,setTimeout传入的参数没有被接收,此时回调里的
data会沿着作用域链去外部查找,取的是全局的data。而setTimeout是异步任务,要等所有同步代码执行完才会运行,同步代码最后把data改成了3,所以回调执行时取到的是3,最终输出1 3 3。
2. Promise封装代码输出1 3 3的原因
Promise的执行器函数(new Promise里的第一个函数)是同步执行的,但里面的setTimeout是异步任务。
- 当你写
resolve(data)时,这里的data并没有被立即求值,而是等到2秒后setTimeout的回调执行时,才会去外部作用域查找data的当前值。 - 同步代码中,创建Promise后马上把
data改成了3,等异步任务执行时,data已经是3了,所以resolve的是3,then回调输出3,最终结果就是1 3 3。
3. 函数参数对回调函数的可见性
不是不可见,这取决于回调的作用域和参数定义:
- 如果回调函数自己定义了同名参数,那么会优先使用自己的局部参数,和外部函数的参数无关;如果要使用外部参数,需要避免同名或者通过闭包引用。
- 如果回调没有定义同名参数,它可以通过闭包访问外部作用域的变量(包括外部函数的参数),只要这个变量在回调执行时还存在于作用域链中。
内容的提问来源于stack exchange,提问作者Pratyanch Dongre
相关产品推荐
相关产品推荐

