JS单线程的原因
如果多线程同时操作了dom,浏览器并不知道以谁为准。
优点:安全。
缺点:如果有耗时任务,会出现假死现象。
所以为了解决以上问题,JS有俩种模式
同步模式
代码依次执行。
函数的声明不会入CallStack,调用的方法会进入CallStack,执行完成后弹出。可以想象成CallStack就是JS的任务执行表。
如果同步模式遇到耗时操作,可能会卡死,这个时候就需要进行异步解决。
异步模式
开启异步任务后,继续执行同步代码,后续逻辑用回调函数处理。
Timeout开启定时器,进入WebApi后对Timeout说已经完毕,会弹出CallStack,继续执行下一代码。
EventLoop负责调用栈和消息队列。
当CallStack清空后,EventLoop就会从消息队列中取出第一个回调函数压栈。
消息队列中发生了变化,事件循环就会监听到,会就拿到队列的第一个拿出来压栈。
注意JS是单线程(执行代码的是单线程),但浏览器不是。JS某些API也不是单线程的,例如计时器,单独开了线程。
回调函数
异步的本质就是回调,例如 你根本不知道这个异步任务何时完成,这个时候就需要一个方法来通知你。
Promise
Promise是个对象。
Promise只有三种状态,待定 --》 成功或者失败
// Promise 基本示例
const promise = new Promise(function (resolve, reject) {
// 这里用于“兑现”承诺
// resolve(100) // 承诺达成
reject(new Error('promise rejected')) // 承诺失败
})
promise.then(function (value) {
// 即便没有异步操作,then 方法中传入的回调仍然会被放入队列,等待下一轮执行
console.log('resolved', value)
}, function (error) {
console.log('rejected', error)
})
console.log('end') 最先执行
注意:及时.then没有回调函数也会进入队列排队。
new的时候 promise就会被执行了。
Promise 方式的 AJAX
API
users.json
[
{
"name": "zce",
"age": 24
},
{
"name": "alan",
"age": 25
}
]
urls.json
{
"users": "/api/users.json",
"posts": "/api/posts.json"
}
posts.json
[
{
"title": "Hello world",
"body": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
},
{
"title": "25 forever",
"body": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
}
]
Promise 方式的 AJAX
// 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/foo.json').then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
Promise 常见误区
// Promise 常见误区
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()
})
}
// 嵌套使用 Promise 是最常见的误区
// ajax('/api/urls.json').then(function (urls) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// })
// })
// })
// })
// })
Promise 链式调用
// Promise 链式调用
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')
// var promise2 = promise.then(
// function onFulfilled (value) {
// console.log('onFulfilled', value)
// },
// function onRejected (error) {
// console.log('onRejected', error)
// }
// )
// console.log(promise2 === promise) false .then后返回全新的promise
ajax('/api/users.json')
.then(function (value) {
console.log(1111)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(2222)
console.log(value)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(3333)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(4444)
return 'foo'
}) // => Promise
.then(function (value) {
console.log(5555)
console.log(value)
})
Promise 异常处理
// Promise 异常处理
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// }, function onRejected (error) {
// console.log('onRejected', error)
// })
// 使用 catch 注册失败回调是更常见的
// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// })
// .catch(function onRejected (error) {
// console.log('onRejected', error)
// })
// then(onRejected) 实际上就相当于 then(undefined, onRejected)
// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// })
// .then(undefined, function onRejected (error) {
// console.log('onRejected', error)
// })
// 同时注册的 onRejected 只是给当前 Promise 对象注册的失败回调
// 它只能捕获到当前 Promise 对象的异常
// ajax('/api/users.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// return ajax('/error-url')
// }, function onRejected (error) {
// console.log('onRejected', error)
// })
// 因为 Promise 链条上的任何一个异常都会被一直向后传递,直至被捕获
// 分开注册的 onRejected 相当于给整个 Promise 链条注册失败回调
ajax('/api/users.json')
.then(function onFulfilled (value) {
console.log('onFulfilled', value)
return ajax('/error-url')
}) // => Promise {}
// .catch(function onRejected (error) {
// console.log('onRejected', error)
// })
// 全局捕获 Promise 异常,类似于 window.onerror
window.addEventListener('unhandledrejection', event => {
const { reason, promise } = event
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的 Promise 对象
event.preventDefault()
}, false)
// Node.js 中使用以下方式
// process.on('unhandledRejection', (reason, promise) => {
// console.log(reason, promise)
// // reason => Promise 失败原因,一般是一个错误对象
// // promise => 出现异常的 Promise 对象
// })
catch和没有catch 的区别
链式调用下
常用 Promise 静态方法
// 常用 Promise 静态方法
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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()
})
}
// Promise.resolve('foo')
// .then(function (value) {
// console.log(value)
// })
// new Promise(function (resolve, reject) {
// resolve('foo')
// })
// 如果传入的是一个 Promise 对象,Promise.resolve 方法原样返回
// var promise = ajax('/api/users.json')
// var promise2 = Promise.resolve(promise)
// console.log(promise === promise2) true
// 如果传入的是带有一个跟 Promise 一样的 then 方法的对象,
// Promise.resolve 会将这个对象作为 Promise 执行
// Promise.resolve({
// then: function (onFulfilled, onRejected) {
// onFulfilled('foo')
// }
// })
// .then(function (value) {
// console.log(value)
// })
// Promise.reject 传入任何值,都会作为这个 Promise 失败的理由
// Promise.reject(new Error('rejected'))
// .catch(function (error) {
// console.log(error)
// })
Promise.reject('anything')
.catch(function (error) {
console.log(error)
})
Promise 并行执行
如果没有互相依赖,就可以并行请求,节省时间。
// Promise 并行执行
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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')
// ajax('/api/posts.json')
// var promise = Promise.all([
// ajax('/api/users.json'),
// ajax('/api/posts.json')
// ])
// promise.then(function (values) {
// console.log(values)
// }).catch(function (error) {
// console.log(error)
// })
// ajax('/api/urls.json')
// .then(value => {
// const urls = Object.values(value)
// const tasks = urls.map(url => ajax(url))
// return Promise.all(tasks)
// })
// .then(values => {
// console.log(values)
// })
// Promise.race 实现超时控制
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('timeout')), 500) 常用语设置超时的方法
})
Promise.race([
request,
timeout
])
.then(value => {
console.log(value)
})
.catch(error => {
console.log(error)
})
微任务 宏任务
// 微任务
console.log('global start')
// setTimeout 的回调是 宏任务,进入回调队列排队
setTimeout(() => {
console.log('setTimeout')
}, 0)
// Promise 的回调是 微任务,本轮调用末尾直接执行
Promise.resolve()
.then(() => {
console.log('promise')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
console.log('global end')
settimeout是宏任务,宏任务有了新的宏任务后 会到最后尾继续排队,微任务会![在同步任务结束后执行](https://i-blog.csdnimg.cn/blog_migrate/5ec23a3dcc87242eba77af2bd31c1277.png#pic_center)
但Promise和MutaitionObserver还有node的process.nexttick是微任务,在本轮调用的末尾就执行了。
generator
promise还是会有.then多层,可读性还是没有同步代码那么高,这个时候我们还有更优的解决方案,generator和 Async / Await。generator可以作为了解。今后开发都会 Async / Await为主了。
// 生成器函数回顾
function * foo () {
console.log('start')
try {
const res = yield 'foo'
console.log(res)
} catch (e) {
console.log(e)
}
}
const generator = foo() 不会立即执行
const result = generator.next()
/// next后执行 执行到yield位置 把yield后面的值返回出去 foo函数就会暂停下拉
//next会返回一个 yield返回的值
console.log(result)
// generator.next('bar') 在yield暂定的位置继续执行,这里的bar会作为yield的返回值
generator.throw(new Error('Generator error'))
Generator 配合 Promise 的异步方案
// Generator 配合 Promise 的异步方案
function ajax (url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.statusText))
}
}
xhr.send()
})
}
function * main () {
try {
const users = yield ajax('/api/users.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls11.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
function co (generator) {
const g = generator()
function handleResult (result) {
if (result.done) return // 生成器函数结束
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
co(main) 15年前比较流行
// const result = g.next()
// result.value.then(data => {
// const result2 = g.next(data)
// if (result2.done) return
// result2.value.then(data => {
// const result3 = g.next(data)
// if (result3.done) return
// result3.value.then(data => {
// g.next(data)
// })
// })
// })
Async / Await 语法糖
// Async / Await 语法糖
function ajax (url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.statusText))
}
}
xhr.send()
})
}
function co (generator) {
const g = generator()
function handleResult (result) {
if (result.done) return // 生成器函数结束
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
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)
}
}
// co(main)
const promise = main()
promise.then(() => {
console.log('all completed')
})