文章内容输出来源:拉勾教育大前端高薪训练营 和自我总结
JS是单线程语言
- JS执行环境中负责执行代码的线程只有一个
- 执行任务的模式有两种:同步模式(Synchronous)、异步模式(Asynchronous)
优点:更安全更简单
缺点:遇到耗时任务,之后任务需要排队,会出现拖延假死的现象
这里的同步/异步是指运行环境提供的API是以同步或者异步模式的方式工作
同步模式(Synchronous)
同步执行模式指的是代码中任务依次执行,后一个任务必须等前一个任务执行完成后才能开始执行,程序执行顺序和代码编写顺序一致,这种方式相对简单,在单线程情况,大多数情况下都是以同步模式执行
异步模式(Asynchronous)
异步执行模式不会等待这个任务的结束才开始下一个任务,开启过后就立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义
异步模式的重要性:没有异步模式,单线程的JavaScript语言就无法同时处理大量耗时任务
回调函数()
回调函数是所有异步实现的根基
Promise
基本用法
//Promise 基本示例
const promise = new Promise(function (resolve, reject) {
//这里用于兑现承诺
// resolve(100) // 承诺达成
reject(new Error('promise rejected')) //承诺失败
})
promise.then(function (value) {
console.log('resolved', value)
},function (error) {
console.log('rejected',error)
})
console.log('end') // 先打印出end,再打印Error
Promise封装ajax
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status ===200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('./api/users.json').then(function (res) {
console.log(res)
},function (error) {
console.log(error)
})
Promise通过链式调用避免回调嵌套
- Promise对象的then方法会返回一个全新的Promise对象
- 后面的then方法就是在为上一个then返回的Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调返回的是Promise,那后面then方法的回调会等待它的结束
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status ===200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
var promise = ajax('./api/users.json')
// then 方法返回一个全新的promise对象
var promise2 = promise.then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
console.log(promise2 === promise) // false
// 每一个then方法都是在为上一个then方法添加状态明确过后的回调
ajax('./api/users.json')
.then(function (value) {
console.log(111)
return ajax('./api/users.json')
}) // => Promise
.then(function (value) {
console.log('shuju1', value) //Array(2)
console.log(222)
return 'foo'
}) // => Promise
.then(function (value) {
console.log(333)
console.log('shuju2', value) // foo
}) // => Promise
.then(function (value) {
console.log(444)
console.log('shuju3', value) //undefined
}).catch(function onRejected(error) {
console.log('onRejected', error)
})
其中catch也是then的别名
.catch(function onRejected(error) {
console.log('onRejected', error)
})
// 就相当于
.then(undefined, function (value) {
console.log(444)
console.log('数据3', value)
})
then中的第二个参数是reject函数,catch中的参数也是reject函数,但是作用不太相同,then中的reject不能捕获到第一个参数中的resolve中的异常,但是catch由于链式作用,能捕获到前面任意处的异常
ajax('/api/users.json')
.then(function (value) {
console.log(111)
return ajax('./api/users.json') // 这个异常无法捕获
}, function onRejected (e){
console.log('reject', e)
})
推荐使用catch捕获异常,可以捕获整个promise链条上的异常:
ajax('/api/users.json')
.then(function (value) {
console.log(111)
return ajax('./api/users.json') // 这个异常无法捕获
}) .catch(function onRejected (e){
console.log('reject', e)
})
还可以全局捕获异常:
浏览器中,在window对象上注册事件:
window.addEventListener('unhandledrejection', event => {
const {reason, promise} = event
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的Promise对象
event.preventDefault()
}, false)
node中:
process.on('unhandledRejection', (reason, promise) => {
const {reason, promise} = event
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的Promise对象
})
尽量在代码中明确捕获每一个可能的异常,而不是丢给全局处理。
Promise静态方法
Promise.resolve() 快速的把一个值转化为Promise对象
Promise.resolve('foo')
.then(value=>{
console.log(value)
})
//Promise.resolve('foo') 等价于
new Promise((resolve, reject) => {
resolve('foo')
})
var promise = ajax('/api/users.json')
var promise = Promise.resolve(promise)
console.log(promise === promise2) //true
//带then方法的对象,就是实现了thenable接口,可以被then的对象
Promise.resolve({
then:((onFulfilled, onRejected) => {
onFulfilled('foo')
})
}).then(value => {
console.log(value) //foo
})
Promise.reject(err) 传入的对象为失败的原因
Promise.reject('anything')
.catch(err=>{
console.log(err) //anything
})
Promise并行执行
Promise.all() 等待所有任务成功结束了,才算结束
var promise = Promise.all([
ajax('/api/users.json'),
ajax('/api/posts.json'),
])
//只有promise里面的每一个任务都执行城关了才进入resolve
//如果其中任何一个失败了,都会进入catch
promise.then(values => {
console.log(values)
// Array(2)
// 0:
// username: "yibo"
// __proto__: Object
// 1:
// name: "jiailing"
}).catch(err => {
console.log(err)
})
ajax('/api/urls.json')
.then(value => {
const urls = Object.values(value)
const tasks = urls.map(url => ajax(url))
return Promise.all(task)
}).then(values => {
console.log(values)
})
Promise.race() 只会等待第一个结束的任务
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
setTime(() => {
reject( new Error('timeout'))
}, 500)
})
Promise.race([request, timeout])
.then(value => {
console.log(value)
}).catch(err => {
console.log(err)
})
//试下ajax请求超时控制的一种方式
微任务
即使Promise中没有任何异步操作,它的回调函数仍然会进入到回调队列中排队。必须等待所有同步代码执行完毕后,Promise中的代码才会被调用。
JS回调队列中的任务称之为宏任务,而宏任务执行过程中可以临时加上一些额外需求,可以选择作为一个新的宏任务进入到队列中排队(如:setTimeout),也可以作为当前任务的微任务。直接在当前任务结束后立即执行
Promise的回调会作为微任务执行。微任务的目的是为了提高整体的响应能力,目前绝大多数异步调用都是作为宏任务执行,Promise、MutationObserver、process.nextTick是作为微任务在本轮调用的末尾执行
console.log('global') //第一个打印
Promise.resolve()
.then(() => {
console.log('promise1') //第3个打印
})
.then(() => {
console.log('promise2') //第4个打印
})
.then(() => {
console.log('promise3') //第5个打印
})
console.log('global end') //第2个打印
setTimeout属于宏任务
console.log('global') //第一个打印
setTimeout(()=>{
console.log('last') //第6个打印
},0)
Promise.resolve()
.then(() => {
console.log('promise1') //第3个打印
})
.then(() => {
console.log('promise2') //第4个打印
})
.then(() => {
console.log('promise3') //第5个打印
})
console.log('global end') //第2个打印
console.log('global start') //第1个打印
Generator异步方案
Generator的基本使用
生成器函数会返回一个生成器对象,调用这个生成器对象的next方法,才会让函数体执行,一旦遇到了yield关键词,函数的执行则会暂停下来,yield后面的值作为next函数的结果返回,如果继续调用函数的next函数,则会在上一次暂停的位置继续执行,直到函数体执行完成,next返回的对象的done就会变成true
function * fn() {
console.log(111)
yield 100
console.log(222)
yield 200
console.log(333)
yield 300
}
const generator = fn()
console.log(generator.next())
// 111
// {value:100,done:false}
console.log(generator.next())
// 222
// {value:200,done:false}
console.log(generator.next())
// 333
// {value:300,done:false}
Generator实现异步
注意:generator.next(value)中,next传入的参数会作为上一次yield的返回值。
function ajax(url){
return new Promise((resolve, reject) => {
var xhr = XMLHttpRequest()
xhr.open("GET",url)
xhr.responseType='json'
xhr.onload = function () {
if(this.status = 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
//生成器函数
function * main() {
const users = yield ajax('/api/user.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls.json')
console.log(urls)
}
//调用生成器函数得到一个生成器对象
const generator = main()
//递归实现generator.next()的调用,知道done为true终止
function dfs(value) {
const result = generator.next(value)
if(result.done) return
result.value.then(data => {
console.log(data)
dfs(data)
})
}
dfs()
// 打印结果
// Generator实现异步.js:35 {username: "yibo"}
// Generator实现异步.js:19 {username: "yibo"}
// Generator实现异步.js:35 {posts: "jiailing"}
// Generator实现异步.js:22 {posts: "jiailing"}
// Generator实现异步.js:35 {posts: "/api/posts.json", users: "/api/users.json"}
// Generator实现异步.js:25 {posts: "/api/posts.json", users: "/api/users.json"}
封装生成器函数执行器co
function ajax(url){
return new Promise((resolve, reject) => {
var xhr = XMLHttpRequest()
xhr.open("GET",url)
xhr.responseType='json'
xhr.onload = function () {
if(this.status = 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
//生成器函数
function * main() {
const users = yield ajax('/api/user.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls.json')
console.log(urls)
}
//调用生成器函数得到一个生成器对象
const generator = main()
//封装了一个生成器函数的执行器
function co(main) {
//调用生成器函数得到一个生成器对象
const generator = main()
//递归实现generator.next()的调用,直到done为true终止
function handleResult(result){
if(result.done) return
result.value.then(data => {
console.log(data)
handleResult(generator.next(data))
},err => {
generator.throw(err)
})
}
handleResult((generator.next()))
}
co(main)
// 打印结果
// Generator实现异步.js:42 {username: "yibo"}
// Generator实现异步.js:20 {username: "yibo"}
// Generator实现异步.js:42 {posts: "jiailing"}
// Generator实现异步.js:23 {posts: "jiailing"}
// Generator实现异步.js:42 {posts: "/api/posts.json", users: "/api/users.json"}
// Generator实现异步.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
Async/Await 语法糖
await关键词只能出现在async函数中。
function ajax(url){
return new Promise((resolve, reject) => {
var xhr = XMLHttpRequest()
xhr.open("GET",url)
xhr.responseType ='json'
xhr.onload = function () {
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
async function main(){
try{
const users = await ajax('/api/users.json')
console.log(users)
const posts = await ajax('/api/posts.json')
console.log(posts)
const urls = await ajax('/api/urls.json')
console.log(urls)
}catch (e) {
console.log(e)
}
}
main()
// async-await.js:20 {username: "yibo"}
// async-await.js:23 {posts: "jiailing"}
// async-await.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
网友评论