JS是单线程的
原因
JS是浏览器脚本语言,要和用户做频繁的实时互动和对dom进行操作,多线程会带来很复杂的同步问题。
如:线程1准备更新某个p标签,这时线程2将该p标签删除了,这就带来程序运行的错误。
alert函数
alert函数会暂停当前主程序的执行,同时暂停计时,点击确定后恢复。
事件轮询机制
JS代码从进程的角度可分为初始化代码和回调代码,所有代码都在执行栈中执行。js引擎启动后先把初始化代码全部执行一遍(包含一些特别代码,设置定时器、绑定事件监听、发送ajax请求),随后才可能执行回调代码里的回调函数(称为异步执行)。
当执行栈中无待执行程序后,首先检查微任务队列,将微任务队列中的任务依次放入执行栈中执行,直到清空微任务队列,然后检查宏任务队列,将宏任务队列的第一个任务放入执行栈执行,并且每完成一个宏任务就会去检查微任务队列,将微任务队列中堆积的任务全部依次执行完毕后再回到宏任务队列执行下一个宏任务,如此反复。
补充:
1.每次清空微任务队列后,如果DOM结构发生变化,会尝试DOM渲染。
2.微任务是ES6语法规定的,宏任务是浏览器规定的。
Promise对象
Promise 是 ES6 新增加的
new Promise(function(resolve, reject){ //构造Promise对象时,输入参数为一个函数
//do something //该函数里面的内容会被立即执行
if(没有出错){
resolve(v1,v2....); //程序正常运行时resolve被调用,随后作为微任务异步执行
}else{
reject(v3,v4....); //程序异常运行时reject被调用,随后作为微任务异步执行
}
}).then(fun1,fun2); //第一个参数为resolve调用时要执行的回调函数,
//第二个参数为reject被调用时要执行的回调函数
//也可以这样写
a = new Promise(fun1(resolve, reject){});
a.then(fun1,fun2);
//
a.then(fun1).catch(fun2).finally(fun3); //正常执行then,异常执行catch,最后再统一执行finally
//resolve中的输入参数会映射到.then()的回调函数上,作为其传入参数,
//.then()可以链式调用,每次返回的数值能作为下一次回调函数的传入参数,
//then()会按顺序依次执行里面的回调,但是有任何异常都会直接跳到 catch 序列
new Promise(function (resolve, reject) {
console.log(1);
resolve(2);
}).then(function (value) {
console.log(value);
return 3;
}).then(function (value) {
console.log(value);
throw "An error";
}).catch(function (err) {
console.log(err);
});
输出:
1
2
3
An error
//如果then中返回的是一个Promise对象,那么下一个then将相当于对这个返回的Promise进行操作
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
输出:
总经过时间 输出
1000 First
5000 Second
8000 Third
//通过 Promise函数 来更简洁的实现
function fun1(delay, message){
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
}
fun1(1000, 'First').then(return fun1(4000, 'Second')).then(fun1(3000, 'Third'))
Promise属性
promise.state | promise.result |
'pending' | undifined |
'fulfilled' | 结果值 |
'rejected' | error对象 |
resolve函数能将promise.state从'pending'=>'fulfilled',并将resolve(var)中的输入参数作为promise.result的结果值传递出去,从而可以被.then中的回调函数使用。
resolve函数能将promise.state从'pending'=>'rejected',并将reject(err)中的输入参数作为promise.result的error值传递出去,从而可以被.catch中的回调函数使用。
Promise方法:
1.promise.catch(注意小写代表实例的方法),该方法通常等同于promise.then(null, rejection),区别是then(null, rejection)只能捕获当前这个Promise的异常,而在链式调用时在任何一个Promise出现的异常,总能被其后面第一个出现的catch捕获。所以一般建议用catch。
2.Promise.all()与Promise.race()(大写,类的静态方法), eg: var p = Promise.all([p1, p2, p3]);
all()将多个Promise实例包装在一起成为一个新的Promise实例,只有p1, p2, p3的state都变为'fulfilled'时,才执行p.then()里的回调函数,此时p1, p2, p3的结果值组成一个数组作为回调函数的传入参数。而只要p1, p2, p3任一个变成'rejected',p就变为'rejected',此时第一个被'rejected'的实例返回的error值会被传入p.catch的回调函数。
race()将多个Promise实例包装在一起成为一个新的Promise实例,只要p1, p2, p3任一个变成'fulfilled'时,就执行p.then()里的回调函数,此时第一个变为'fulfilled'的Promise实例的结果值会被传入p.then的回调函数。
3.Promise.resolve(var), 静态方法创建一个状态直接为'fulfilled'的Promise实例,如果有var参数,则会作为结果值会被传.then的回调函数,没有就直接执行无输入参数的回调函数。
(但如果var是Promise实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例)
4.Promise.reject(var),同Promise.resolve(var),只不过实例状态为'rejected'。
.then( )链式调用更深入的理解:
then中如果回调函数返回非Promise对象或无返回值时,则then执行完向外返回的值可以看做是Promise.resolve(val),这样才能再直接.then(),同时也保证了上一次then返回出来的值,能直接输入到下一次.then的回调函数中。
即任何promise.then()执行后向外的返回值仍然是Promise实例,从而实现链式调用。
Async与await
异步函数(async function)是ES2017之后的规范
//异步函数async function中可以使用await指令,await指令后必须跟着一个Promise实例,
//异步函数会在这个Promise实例运行中暂停,直到其运行结束再继续运行
async function myDisplay() {
let myPromise = new Promise(function(resolve, reject) {
setTimeout(function() { resolve("Hello"); }, 3000);
});
let promiseResult = await myPromise; //程序运行到这里暂停3秒,然后返回"Hello"
//promiseResult的值为"Hello",因为resolve中的参数会作为Promise实例await之后的返回值
}
myDisplay(); //执行
//也可以直接这样写
async function myDisplay() {
let promiseResult = await new Promise(function(resolve, reject) {
setTimeout(function() { resolve("Hello"); }, 3000);
});
}
//上面Promise实现依次延时执行的async版本,更直观
function fun1(delay, message){
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
}
async function asyncFunc() {
await fun1(1000, "First"); //程序运行到这里延时1秒,然后才会执行下一句
await fun1(4000, "Second"); //程序运行到这里延时4秒,然后才会执行下一句
await fun1(3000, "Third"); //程序运行到这里设置了一个延时3s的定时器,3s后打印最后一个str
}
asyncFunc();
//补充:
//async function会将返回值自动转换成Promise.resolve()创建的实例
async function myFunction() {
return "Hello";
}
//等效于
async function myFunction() {
return Promise.resolve("Hello");
}