JS异步编程
js是单线程语言:js执行环境中,负责执行代码的线程只有一个。为了防止代码运行阻塞,所以js分为同步模式和异步模式。
同步模式
等待任务依次执行,上一个任务结束之后,下一个才执行。
每当一个函数要被执行的时候,这个函数会被进入调用栈,在调用栈中执行完毕之后出栈。
异步模式
不会等待这个任务的结束才去开始下一个任务,开启过后立刻执行下一个任务,后续逻辑一般会用回调函数的方法定义。
调用栈(call stack):函数执行的主进程,可以理解为一个执行中的调用表。
消息队列(Queue):又称回调队列,可以理解为一个即将调用的表。回调队列中的任务称之为宏任务。
event loop事件轮询:监听消息队列和调用栈,当调用栈的程序全部执行完毕之后,就会去查看消息队列是否有程序执行(开始从消息队列中取出第一个消息放入调用栈当中去执行)。
下面举一个例子
当遇见了setTimeout函数时,会将setTimeout函数放入web apis中,当执行栈中的函数全部运行完之后,事件轮询会去查找消息队列中是否有任务执行。
这里例子中web apis中有两个seitimeout函数,当1s后第一个settimeout函数被放入消息队列,事件轮询找到了第一个函数,然后将其放入到执行栈中去执行,1.8s之后第二个settimeout的函数被放入到消息队列中,轮询查找到后被压入到执行栈中执行。由于第二个settimeout函数中还包含了一个settimeout,此时这个新的settimeout又被压入到web api中,等待被放入到消息队列。
Promise
Promise只有状态为resolved或rejected的时候(看是否执行了resolve()或者rejected()函数),then方法才会执行。
注意:Promise.then()返回的依然是一个Promise对象,这样就解决了回调地狱的问题。所以第二个then里面的值就是上一个对象的
const promise = new Promise(function(resolve, reject) {
// resolve(100) //成功,参数作为一个成功之后的返回值
reject(new Error('promise rejected')) //失败,一般返回一个错误的对象
})
promise.then((resolve) => {
//成功之后的回调
console.log(resolve)
}, (error) => {
//失败的回调
console.log(error)
})
例子:用promise实现一个ajax请求
function ajax(url) {
return new Promise(function(resolve, rejected) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200) {
resolve(this.response)
} else {
rejected(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('./api/user.json').then((resolve) => {
console.log(resolve)
}, (rejected) => {
console.log(rejected)
})
Promise异常处理
只要promise函数有报错,then方法都会异常处理,并不只是在promise函数的reject里面写才会用作异常处理。
Promise异常处理中,链式调用的时候,如果只用then方法进行异常捕获,那其实后面的then方法捕获的是前一个的异常,并不是捕获的自身的异常,因为then方法是前一个promise的回调。所以在promise异常处理中,一般都是用catch方法做异常捕获,catch方法捕获的是当前回调的异常,不是上一个回调的异常。
ajax('./api/user.json')
.then((resolve) => {
console.log(resolve)
// return ajax('./api/uss.json')
})
.then((resolve) => {
console.log(resolve)
return ajax('./api/uss22.json') //不存在
})
.then((resolve) => {
console.log(resolve)
return ajax('./api/uss33.json') //不存在
})
.catch((rejected) => {
console.log(rejected)
})
Promise静态方法
Promise.resolve()
快速的把一个值转化为一个Promise对象。如果传值传的本身就是一个Promise对象,那么则返回这个Promise对象。如果传入了一个对象,这个对象中有then函数,那么可以将这个函数作为一个Promise返回
Promise.reject()
快速把一个值转化法一个一定失败的promise对象
Promise.resolve('foo').then( resolve => {console.log(resolve)})
//等同于
new Promise(function(resolve, reject) {
resolve('foo')
})
Promise.resolve({
then: function(onFullfilled, onRejected) {
onFullfilled('foo')
}
}).then((resolve) => {
console.log(resolve) //foo
})
Promise并行执行
Promise.all()
同时进行,全部任务结束才会返回值。接收一个全是Promise对象的数组,返回一个全新的Promise对象,拿到的结果是一个数组,每个值都是传入的Promise的结果。如果有一个Promise失败,那么会直接返回。
Promise.race()
只会等待第一个结束任务。用于类似AJAX请求超时的实现
Generator异步方案
yield 返回的内容作为.next()的结果
function * main () {
yield ajax('./api/user.json')
yield ''
}
const g = main()
const result = g.next()
result.value.then(data => {
// console.log(data)
const result2 = g.next()
console.log(result2.value)
})
Generator执行器
function * main () {
try {
yield ajax('./api/user.json')
yield ajax('./api/user1.json')
} catch (e) {
console.log(e)
}
}
// const result = g.next()
// result.value.then(data => {
// // console.log(data)
// const result2 = g.next()
// console.log(result2.value)
// })
function co (generator) {
const g = generator()
function handleResult (result) {
if(result.done) return //生成器结束
result.value.then(data => {
handleResult(g.next())
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
co(main)
Aysnc/Await语法糖
Aysnc其实是生成器的一个语法糖,返回一个Promise对象,这个函数的返回值是这个promise状态值的resolve值。
await是一个表达式,如果后面不是一个promise对象,就直接返回对应的值。那如果后面是一个promise对象,await将等待这个promise对象的resolve状态值的value,且将这个值返回前面的变量。
async function main () {
try {
await ajax('./api/user.json')
console.log(1)
await ajax('./api/user.json')
console.log(2)
} catch (e) {
console.log(e)
}
}
const g = main()
g.then(data => {
console.log(data)
})
本节过程中所使用的工具
html-webpack-pluging
1.为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
2.可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口