Skip to content

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时不可调用
    • 不能被多次调用
  • onRejected
    • 必须在状态为rejected后才能调用,并将reason作为他的第一个参数
    • pending时不可调用
    • 不能被多次调用

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)
  })
}

KESHAOYE-知识星球