异步、循环事件
JS是单线程,碰见同步执行同步(promise本身、js外层代码)直到执行完毕,遇到异步放到执行队列中去,异步(宏任务和微任务),在异步中微任务是优于宏任务执行的
1、js宏任务有:
<script>
整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM
2、js微任务有:
process.nextTick、MutationObserver、Promise.then catch finally
例题
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
输出结果如下:
1
看到这个题目,好多的then,实际上只需要记住一个原则:.then 或.catch 的参数期望是函数,传入非函数则会发生值透传。
第一个then和第二个then中传入的都不是函数,一个是数字,一个是对象,因此发生了透传,将resolve(1) 的值直接传到最后一个then里,直接打印出1。
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log('result: ', res))
.catch(err => console.log(err))
输出结果如下:
1
'result: ’ 1
2
3
then只会捕获第一个成功的方法,其他的函数虽然还会继续执行,但是不是被then捕获了。
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log('start')
输出结果如下:
async1 start
async2
start
async1 end
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
输出结果如下:
script start
async1 start
promise1
script end
这里需要注意的是在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的 .then
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
输出结果如下:
1
2
4
timerStart
timerEnd
success
Promise.resolve().then(() => {
console.log('1');
throw 'Error';
}).then(() => {
console.log('2');
}).catch(() => {
console.log('3');
throw 'Error';
}).then(() => {
console.log('4');
}).catch(() => {
console.log('5');
}).then(() => {
console.log('6');
});
执行结果如下:
1
3
5
6
在这道题目中,我们需要知道,无论是thne还是catch中,只要throw 抛出了错误,就会被catch捕获,如果没有throw出错误,就被继续执行后面的then。
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
输出success1
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res); 只有执行了resolve,then才会执行。
});
console.log(4);
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {
console.log('timer3')
}, 0)
console.log("start")
输出:
async1 start
async2
start
async1 end
timer2
timer3
timer1
总结:
1.promise.race 会再多个中返回最早输出结果得哪个,但是不影响其他较慢得函数执行,只是不会被promise.then 捕捉到
2.async 本身是同步得,等待得await是异步得,会等到这个异步函数执行完以及下面得同步代码执行完再继续执行
3.如果promise得状态没有变化,不会执行下面代码
4.Promise的状态在发生变化之后,就不会再发生变化。同时下面的catch也不会捕获到错误
5.Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值
this指向问题
var a = 10
var obj = {
a: 20,
say: () => {
console.log(this.a)
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
输出结果:10 10
我么知道,箭头函数时不绑定this的,它的this来自原其父级所处的上下文,所以首先会打印全局中的 a 的值10。后面虽然让say方法指向了另外一个对象,但是仍不能改变箭头函数的特性,它的this仍然是指向全局的,所以依旧会输出10。
function a() {
console.log(this);
}
a.call(null);
打印结果:window对象
根据ECMAScript262规范规定:如果第一个参数传入的对象调用者是null或者undefined,call方法将把全局对象(浏览器上是window对象)作为this的值。所以,不管传入null 还是 undefined,其this都是全局对象window。所以,在浏览器上答案是输出 window 对象。
var obj = {
name : 'cuggz',
fun : function(){
console.log(this.name);
}
}
obj.fun() // cuggz
new obj.fun() // undefined
使用new构造函数时,其this指向的是全局环境window。
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x = 5;
return function() {
return this.x;
}();
},
getY: function() {
var y = 7;
return this.y;
}
}
console.log(obj.getX()) // 3
console.log(obj.getY()) // 6
输出结果:3 6
解析:
我们知道,匿名函数的this是指向全局对象的,所以this指向window,会打印出3;
getY是由obj调用的,所以其this指向的是obj对象,会打印出6。
(function(){
var x = y = 1;
})();
var z;
console.log(y); // 1
console.log(z); // undefined
console.log(x); // Uncaught ReferenceError: x is not defined
这段代码的关键在于:var x = y = 1; 实际上这里是从右往左执行的,首先执行y = 1, 因为y没有使用var声明,所以它是一个全局变量,然后第二步是将y赋值给x,讲一个全局变量赋值给了一个局部变量,最终,x是一个局部变量,y是一个全局变量,所以打印x是报错。
var obj = {
say: function() {
var f1 = () => {
console.log("1111", this);
}
f1();
},
pro: {
getPro:() => {
console.log(this);
}
}
}
var o = obj.say;
o();
obj.say();
obj.pro.getPro();
o(),o是在全局执行的,而f1是箭头函数,
它是没有绑定this的,它的this指向其父级的this,
其父级say方法的this指向的是全局作用域,所以会打印出window;
obj.say(),谁调用say,say 的this就指向谁,所以此时this指向的是obj对象;
obj.pro.getPro(),我们知道,箭头函数时不绑定this的,getPro处于pro中,
而对象不构成单独的作用域,所以箭头的函数的this就指向了全局作用域window
var myObject = {
foo: "bar",
func: function() {
var self = this;
console.log(this.foo);
console.log(self.foo);
(function() {
console.log(this.foo);
console.log(self.foo);
}());
}
};
myObject.func();
输出结果:bar bar undefined bar
首先func是由myObject调用的,this指向myObject。又因为var self = this;所以self指向myObject。
这个立即执行匿名函数表达式是由window调用的,this指向window 。立即执行匿名函数的作用域处于myObject.func的作用域中,
在这个作用域找不到self变量,沿着作用域链向上查找self变量,找到了指向 myObject对象的self。