目前主流的JavaScript环境都是以单线程模式去执行代码。JavaScript是运行在浏览器端的脚本语言,为了去实现页面的动态交互,而实现页面交互的核心是dom操作,如果在同一时间,不同的线程对dom进行不同的操作,例如一个线程添加dom,一个线程删除dom,这个时候浏览器会不知该如何处理,为了避免这种情况的出现,所以js只能按照顺序从上到下执行,这种从上到下按顺序执行就是同步任务。单线程的JavaScript语言无法同时处理大量耗时任务,这就需要采用异步模式去编写代码。
同步模式与异步模式
同步模式
同步模式指的是代码中的任务依次执行,后一个任务必须等前一个任务执行完毕后才能执行
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
bar()
}
foo()
console.log('global end')
// 输出结果
global begin
foo task
bar task
global end
异步模式
异步模式:不会等待这个任务的结束才开始下一个任务,对于耗时操作都是开启过后就立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义。帮助单线程的js语言同时处理大量的耗时任务。
缺点:代码执行顺序混乱
console.log('global begin')
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
// 输出结果
global begin
global end
timer2 invoke
timer1 invoke
inner invoke
回调函数--所有异步编程方案的根基
回调函数:由调用者定义,交给执行者执行的函数
function foo(callback){
setTimeOut(fucntion(){
callback()
},1000)
}
foo(function(){
console.log('这就是一个回调函数')
console.log('调用这定义这个函数,执行者执行这个函数')
console.log('其实就是调用者告诉执行者异步任务结束后做什么')
})
Promise
promise 就是一个类,在执行这个类的时候需要一个执行器进去,执行器会立即执行
Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
Promise 对象特点
1、**对象的状态不受外界影响。**Promise 对象代表一个异步操作,有三种状态:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
2、**一旦状态改变,就不会再变,任何时候都可以得到这个结果。**Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
promise的使用案例
// 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/user.json').then(function(res){
console.log(res)
},function(err){
console.log(err)
})
/*
then方法内部做的事情是判断状态,如果状态是成功,调用成功回调的函数,如果失败,调用失败的回调函数
then方法是被定义在原型对象中
同一个promise对象下面的then方法是可以被调用多次的,所以then要返回一个全新的promise对象 then方法是可以被链式调用的,后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
.then( ) 里面的实参应该是函数。如果不是函数, 就无视它。
*/
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)
// 输出结果
1
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()
})
}
ajax('/api/user.json').then(function(res){
console.log(111)
return ajax('/api/user.json')
}).then(function(res){
console.log(222)
console.log(res)
return ajax('/api/user.json')
}).then(function(res){
console.log(333)
return ajax('/api/user.json')
}).then(function(res){
console.log(444)
return 'foo'
}).then(function(res){
console.log(555)
console.log(res)
})
//输出信息
111
222
{users:'/api/users.json',posts:'/api/users.json'}
333
444
555
foo
Promise对象的then方法返回一个全新的Promise对象
后面的then方法就是在为上一个then返回的Promise注册回调
前面的then方法中回调函数的返回值会作为后面then方法回调的参数
Promise静态方法
Promise.resolve
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.reject 传入任何值,都会作为这个 Promise 失败的理由
Promise.reject('anything').catch(function (error) { console.log(error) })
Promise.all-等待所有任务结束
同步执行多个promise,当所有的promise都成功结束了,才会成功结束,若是有一个失败,就是以失败告终
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)
})
Promise.race-只会等待第一个结束的任务
// 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)
})
Promise执行时序
// 微任务 直接在当前任务结束过后立即执行,提高整体的响应能力
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')
//输出
global start
global end
promise
promise 2
promise 3
setTimeout
目前绝大多数的异步调用都是作为宏任务执行,但Promise、MutationObserver、process.nextTick作为微任务执行。
Generator
Generator(生成器函数)就是在普通函数基础上用*去声明一个生成器对象,再通过调用.next()去执行这个函数。函数内部可以通过yield去返回一个值,在next方法返回对象中拿到这个值,返回对象中还包含一个done属性,表示这个生成器是否执行完了。如果在next方法中传入一个参数,传入的参数会作为yield语句的返回值。如果外部调用throw方法,也会让生成器函数往下执行,作用是抛出一个异常。
function * foo () {
console.log('start')
const res = yield 'foo'
console.log(res)
}
const generator = foo()
const result = generator.next()
console.log(result)
generator.next('bar')
//输出
start
{value:'foo',done:false}
bar
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()
console.log(result)
generator.throw(new Error('Generator error'))
//输出
start
{value:'foo',done:false}
Error:Generator error
Async Await
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)
}
}
const promise = main()
promise.then(() => { console.log('all completed') })
EventLoop、消息队列都是做什么的?
EventLoop(事件循环机制)负责监听调用栈和消息队列,一旦调用栈所有任务都结束了,事件循环就会从消息队列中取出第一个回调函数,加入消息队列中。
消息队列用来处理异步任务。每当出现异步调用事件时都会将其入队,执行完毕后再由任务队列通知主线程,让JS引擎接管此事件。
什么是宏任务?什么是微任务?
回调队列中的任务是宏任务,一切事件(同步代码块、setTimeout、setInterval等)都是宏任务。而微任务则是直接在当前任务结束过后立即执行的任务,包括Promise、MutationObserver、process.nextTick。顺序的话还是同步事件优先级最高,而这些异步事件其次。当执行宏任务时遇到Promise等,会创建微任务(.then()里的回调),并加入微任务队列队尾。