4.JS中的异步(Promise、async、await)
何为同步异步
JS是单线程语言(渲染主线程)。渲染主线程不但要执行JS还要负责HTML的渲染。同步指的是从头到尾执行代码,遇到网络请求等耗时操作会导致HTML阻塞。异步就应运而生。
promise
Promise A+ 规范
Promise A+是一套规则,告诉你如何实现一个Promise
ES6中推出的Promise是基于Promise A+规范编写的。符合Promise A+规范的对象都可以叫做Promise Like。
Promise的定义
Promise A+规范的定义如下:
- Promise是一个有then方法的对象或者函数,行为符合本规范
- thenable是一个由then方法的对象或函数
- value是Promise状态成功(resolve)时候的值
- reason是Promise状态失败(reject)时候的值
- exception是一个使用throw抛出的异常值
Promise的状态
Promise必须处于三种状态之一:
- pending:等待:还未执行成功,可以转换为成功或失败
- fulfilled:成功:不得转换为其他状态、value必须有值
- rejected:失败:不得转换状态、reason必须有值
then方法
Promise必须实现一个.then
方法来访问最终结果
then方法有两个参数promise.then(onFulfilled, onRejected)
两个参数都必须是函数,否则将被忽略
- onFulfilled
- 必须在状态为fulfilled后才能调用,并将
value
作为他的第一个参数 - pending时不可调用
- 不能被多次调用
- 必须在状态为fulfilled后才能调用,并将
- onRejected
- 必须在状态为rejected后才能调用,并将
reason
作为他的第一个参数 - pending时不可调用
- 不能被多次调用
- 必须在状态为rejected后才能调用,并将
then方法必须返回一个Promise
简单实现
js
function MyPromise(callback){
this._state = 'pending'
this._value = undefined
this._reason = undefined;
this._cache = []
function _resolve(data) {
this.changeState('fulfilled', data)
}
function _reject(data) {
this.changeState('rejected', data)
}
function _run() {
if(this._state === 'pending') return;
while(this._cache.length) {
const {onFulfilled, onRejected, resolve, reject} = this._cache.shift();
if(this._state === 'fulfilled') {
if(typeof onFulfilled === 'function') {
try {
const data = onFulfilled(this._value)
resolve(data)
} catch(err) {
reject(err)
}
} else {
resolve(onFulfilled)
}
} else {
if(typeof onRejected === 'function') {
const reason = onRejected(this._reason)
reject(reason)
} else {
reject(onRejected)
}
}
}
}
function changeState(state, data) {
if(this._state === 'pending') {
this._state = state
this._value = data
this._run()
}
}
try {
callback(_resolve, _reject)
} catch(e) {
this.changeState('rejected',e)
}
function then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject)=>{
this._cache.push({
onFulfilled,
onRejected,
resolve,
reject
})
if(this._state === 'fulfilled') {
onFulfilled(this._value)
} else if(this._state === 'rejected') {
onRejected(this._reason)
}
})
}
}
Promise的细节
- resolve()、reject()不会中断函数的执行
js
console.log('strt')
const p = new Promise((resolve, reject) => {
console.log(1)
resolve(2)
console.log(3)
})
p.then(res => {
console.log(res)
})
console.log('end')
结果为 start 1 3 end 2
- 微任务优先级最高
js
console.log('start')
const promise1 = Promise.resolve().then(() => {
console.log('promise1')
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
})
const timer1 = setTimeout(() => {
console.log('timer1')
const promise2 = Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('end')
结果为 start end promise1 timer1 promise2 timer2
async/await
async
简单来说,一个函数加上async就会返回一个Promise。async函数的返回值可以看成由Promise.resolve()包裹的。
js
async function a() {
return 1
}
console.log(a()) // Promise{<fulfilled> : "1"}
await
await只能在async函数中使用。await会阻塞代码的执行,在异步执行完成后,继续向下执行。
原理
async/await是参照Generator设计出的一套异步处理方案。
Generator
Generator函数是ES6提供的一种异步编程解决方案。Generator函数返回一个遍历器对象(Iterator),返回的遍历器对象可以依次遍历Generator函数内部的每一个状态。
Generator函数有两个特征:
- function关键字和函数名之间有一个*号
- 函数体内部使用yield表达式
js
function * testGenerator() {
yield 'hello'
yield 'world'
return 'end'
}
调用函数时,函数并不执行,而是返回一个Iterator对象(指向内部状态的指针对象)。调用next
方法可以使指针对象移向下一个状态(yield或return)。
js
const tg = testGenerator()
tg.next()
// {value: 'hello', done: false}
tg.next()
// {value: 'world', done: false}
tg.next()
// { value: 'end', done: true }
实现
js
function * asyncfunction(fn) {
return new Promise((resolve, reject) => {
const root = fn()
function next() {
// ...
Promise.resolve(result.value).then(res=>{
next(res)
})
}
next(undefined)
})
}