一、JS代码执行机制
JavaScript的单线程
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
同步任务和异步任务
单线程导致的问题就是后面的任务等待前面任务完成,如果前面任务很耗时(比如读取网络数据),后面任务不得不一直等待!!
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务和异步任务。
JS中所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
- 【同步任务】指的是:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 【异步任务】指的是:不进入主线程、而进入“任务队列”的任务,当主线程中的任务运行完了,才会将异步任务相关的回调函数从”任务队列”取出异步任务放入主线程执行。
异步任务又分为宏任务和微任务
宏任务
- 主代码块
- setTimeout
- setInterval
微任务
- process.nextTick ()
- Promise
- Object.observe
事件循环机制
- 碰到同步任务,就先执行执行栈中的同步任务
遇到函数的嵌套调用就把函数压入栈内再依次“剥洋葱”
- 碰到异步任务就压入任务队列(异步任务分为宏任务和微任务)
- 当前执行栈中的所有同步任务执行完毕,就将执行异步任务,异步任务执行原则“先微后宏”
由于主线程不断地重复获得任务、执行任务、再获取任务、再执行……,这种机制被称为事件循环(Event Loop)
实例
const promise = new Promise((resolve, reject) => {
// new 先行
console.log(1);
resolve("success");
console.log(2);
});
promise.then((data) => {
// 微任务
console.log(data);
console.log(3);
});
console.log(4);
1
2
4
success
3
const promise1 = new Promise((resolve, reject) => {
console.log("promise1");
resolve("resolve1");
});
const promise2 = promise1.then((res) => {
console.log(res);
});
console.log("1", promise1);
console.log("2", promise2);
promise1
1 Promise { 'resolve1' }
2 Promise { <pending> }
resolve1
测试代码
// setTimeout(()=>{
// console.log(1)
// },0)
// new Promise((resolve) =>{
// console.log(2)
// resolve()
// }).then(()=>{ // 此时将then的内容放到微任务队列,然后执行同步代码 5
// console.log(3)
// }).then(()=>{
// console.log(4)
// })
// console.log(5)
// setTimeout(() => {
// console.log('timer1');
// setTimeout(() => {
// console.log('timer3')
// }, 0)
// }, 0)
// setTimeout(() => {
// console.log('timer2')
// }, 0)
// console.log('start')
// setTimeout(() => {
// console.log('timer1');
// Promise.resolve().then(() => {
// console.log('promise')
// })
// }, 0)
// setTimeout(() => {
// console.log('timer2')
// }, 0)
// console.log('start')
// const first = () => (new Promise((resolve, reject) => {
// console.log(3)
// let p = new Promise((resolve, reject) => {
// console.log(7)
// setTimeout(() => {
// console.log(5)
// resolve(6) // 状态只会改变一次
// }, 0)
// resolve(1)
// })
// resolve(2)
// p.then((arg) => {
// console.log(arg)
// })
// }))
// first().then((arg) => {
// console.log(arg)
// })
// console.log(4) // 在1 2 之前执行,此时1 2都在微任务队列里面
// setTimeout(() => {
// console.log("0")
// }, 0)
// new Promise((resolve,reject)=>{
// console.log("1")
// resolve()
// }).then(()=>{ // then碰到then,就将其全部放到微任务队列,再细致考虑
// console.log("2")
// new Promise((resolve,reject)=>{
// console.log("3")
// resolve()
// }).then(()=>{
// console.log("4") // 先5后4 “队列”
// }).then(()=>{
// console.log("5")
// })
// }).then(()=>{
// console.log("6")
// })
// new Promise((resolve,reject)=>{
// console.log("7")
// resolve() //2
// }).then(()=>{
// console.log("8")
// })
// Promise.resolve().then(() => {
// console.log('promise1');
// const timer2 = setTimeout(() => {
// console.log('timer2')
// }, 0)
// });
// const timer1 = setTimeout(() => {
// console.log('timer1')
// Promise.resolve().then(() => {
// console.log('promise2')
// })
// }, 0)
// console.log('start');
// const promise1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('success')
// }, 1000)
// })
// const promise2 = promise1.then(() => {
// throw new Error('error!!!')
// })
// console.log('promise1', promise1)
// console.log('promise2', promise2)
// setTimeout(() => {
// console.log('promise1', promise1)
// console.log('promise2', promise2)
// }, 2000)
异步编程
Promise
- 理解并能准确判断代码执行时机
测试代码
/**
* Promise:resolve reject then catch finally
*/
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('success')
// },1000)
// })
// let p2 = new Promise((resolve, reject) => {
// setTimeout(() => {
// reject('failed')
// }, 500)
// })
// Promise.race([p1, p2]).then((result) => {
// console.log(result)
// }).catch((error) => {
// console.log(error) // 打开的是 'failed'
// })
// function test(resolve, reject) {
// let timeOut = Math.random() * 2;
// console.log('set timeout to: ' + timeOut + ' seconds.');
// setTimeout(function () {
// if (timeOut < 1) {
// console.log('call resolve()...');
// resolve('200 OK');
// }
// else {
// console.log('call reject()...');
// reject('timeout in ' + timeOut + ' seconds.');
// }
// }, timeOut * 1000);
// }
// let p1 = new Promise(test);
// let p2 = p1.then(function (result) {
// console.log('成功:' + result);
// });
// let p3 = p2.catch(function (reason) {
// console.log('失败:' + reason);
// });
// function test(resolve, reject) {
// let timeOut = Math.random() * 2;
// console.log('set timeout to: ' + timeOut + ' seconds.');
// setTimeout(function () {
// if (timeOut < 1) {
// console.log('call resolve()...');
// resolve('200 OK');
// }
// else {
// console.log('call reject()...');
// reject('timeout in ' + timeOut + ' seconds.');
// }
// }, timeOut * 1000);
// }
// new Promise(test)
// .then(function (result) {
// console.log('成功:' + result);
// })
// .catch(function (reason) {
// console.log('失败:' + reason);
// })
// const promise = new Promise((resolve, reject) => {
// reject("error");
// resolve("success2"); // 需要return才能传递结果
// });
// promise.then(res => {
// console.log("then1: ", res); // 需要return才能往下传递结果
// }).then(res => {
// console.log("then2: ", res);
// }).catch(err => {
// console.log("catch: ", err);
// }).then(res => {
// console.log("then3: ", res); // undefined catch也会返回promise对象
// })
// Promise.resolve(1)
// .then(res => {
// console.log(res);
// return 2; // return 2会被包装成resolve(2)
// })
// .catch(err => {
// return 3;
// })
// .then(res => {
// console.log(res);
// });
// const promise = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log('timer')
// resolve('success')
// }, 1000)
// })
// const start = Date.now();
// promise.then(res => {
// console.log(res, Date.now() - start)
// })
// promise.then(res => {
// console.log(res, Date.now() - start) // 两个字几乎一样,状态一旦改变,就不会在变动了
// })
// Promise.resolve().then(() => {
// return new Error('error!!!') // 被包裹成了return Promise.resolve(new Error('error!!!'))
// }).then(res => {
// console.log("then: ", res)
// }).catch(err => {
// console.log("catch: ", err)
// })
/**
* then链式调用
*/
// function runAsync1(){
// var p = new Promise(function(resolve, reject){
// //做一些异步操作
// setTimeout(function(){
// console.log('异步任务1执行完成');
// resolve('随便什么数据1');
// }, 2000);
// });
// return p;
// }
// function runAsync2(){
// var p = new Promise(function(resolve, reject){
// //做一些异步操作
// setTimeout(function(){
// console.log('异步任务2执行完成');
// resolve('随便什么数据2');
// }, 1000);
// });
// return p;
// }
// function runAsync3(){
// var p = new Promise(function(resolve, reject){
// //做一些异步操作
// setTimeout(function(){
// console.log('异步任务3执行完成');
// resolve('随便什么数据3');
// }, 1000);
// });
// return p;
// }
// runAsync1()
// .then(function(data){
// console.log(data);
// return runAsync2();
// })
// .then(function(data){
// console.log(data);
// return runAsync3();
// })
// .then(function(data){
// console.log(data);
// });
/**
* all: 所有异步任务完成执行then
* race:执行快的直接跳出
*/
// function runAsync (x) {
// const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
// return p
// }
// Promise.all([runAsync(1), runAsync(2), runAsync(3)]) // 参数数组都是promise实例,如果不是强制包装
// .then(res => console.log(res))
// function runAsync (x) {
// const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
// return p
// }
// function runReject (x) {
// const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
// return p
// }
// Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
// .then(res => console.log(res))
// .catch(err => console.log(err))
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('success')
// },1000)
// })
// let p2 = new Promise((resolve, reject) => {
// setTimeout(() => {
// reject('failed')
// }, 3000)
// })
// Promise.race([p1, p2]).then((result) => {
// console.log(result)
// }).catch((error) => {
// console.log(error) // 打开的是 'success'
// })
- 手写Promise api
Async、await
await可以看成是Promise的语法糖(实际上是Generate的语法糖),效果和Promise的回调地狱一样,只是代码看起来好看点。
测试代码
/**
* async await
*/
// async function fn () {
// // return await 123
// // 等同于
// return 123
// }
// fn().then(res => console.log(res))
// console.log(fn)
// async function async1() {
// console.log("async1 start");
// await async2();
// console.log("async1 end");
// }
// async function async2() {
// console.log("async2");
// }
// async1();
// console.log('start')
// async function async1() {
// console.log("async1 start");
// await async2(); // 紧跟着await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中
// console.log("async1 end");
// }
// async function async2() {
// setTimeout(() => {
// console.log('timer')
// }, 0)
// console.log("async2");
// }
// async1();
// console.log("start"
// 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")
综合
async function a1() {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2() {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise(resolve => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then(res => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
/** 第一次做错的结果
script start
a1 start
a2
promise1
a1 end
promise2
script end
promise2.then
promise3
setTimeout
*/
/** 正确结果
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
*/
评论区