JS异步

146 阅读7分钟

普通的函数从上往下执行,遇到异步函数会扔到事件队列中,不会阻止主线程执行后面的代码,JS永远不会卡住。

Promise

要点

  • Promise并没有消除回调有then,用async await 消除[then]回调
  • 状态:未决状态(unsettled)[pending]已决状态(settled)[fulfilled,rejected]
  • Promise的执行器参数,抛出错误之后,Promise会自动变成rejected
  • promise的状态一旦确定之后,数据和状态无法改变
  • Promise的构造函数回调参数,直接执行
  • then方法会返回一个新的Promise,新任务的状态取决于后续的处理
    • 若then参数不是函数,没有相关的后续处理函数(对象,数字不是函数),新任务的状态和前任务一致,数据为前任务的数据
    • 若有后续处理但还未执行,新任务挂起
    • 若后续任务执行了,则根据后续处理的情况确定新任务的状态
      • 后续执行无错,新任务的状态为完成,数据为后续处理的返回值
      • 后续执行有错,新任务的状态为失败,数据为异常对象
      • 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致
const pro = new Promise((resolve,reject)=>{
    console.log(a)
})
console.log(pro)
//:Promise {<rejected>: ReferenceError: a is not defined
//    at <anonymous>:2:17
//    at new Promise (<anonymous>)
 //   at <a…}

静态方法

方法名含义
Promise.resolve(data)直接返回一个完成状态的任务
Promise.reject(data)直接返回一个拒绝状态的任务
Promise.all(任务数组)返回一个任务 任务数组全部成功则成功 任何一个失败则失败
Promise.any(任务数组)返回一个任务 任务数组任一成功则成功 任务全部失败则失败
Promise.allSettled(任务数组)返回一个任务 任务数组全部状态已确定则成功 该任务不会失败
Promise.race(任务数组)返回一个任务 任务数组任一状态确定则确定,状态和其一致

async

要点

async用于修饰函数,被它修饰的函数一定返回Promise

async function m(){
    return 123
}
//等于
async function m(){
    return new Promise((resolve)=>resolve(123))
}
console.log(m())//promise {123}
//当成promise使用
m().then(res=>console.log(res))//123

async函数返回promise相当于没有标记async

async function m(){
    return new Promise((resolve)=>{
        setTimeout(()=>{
            resolve(123)
        },1000)
    })
}

async函数内出现错误或者手动thow相当于promise执行reject()

async function m(){
    const data = null
    data.toString()
    return 123
}
cosnole.log(m())//Promise{reject}
//手动reject
async function fune(){
    throw new Error(1)
}
console.log(fune)//Promise{reject}

await

要点

表示等待某个Promise完成,必须用于async函数内

function delay(duration){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve()
        },duration)
    })
}
(async function test(){
    await delay(1000)
})()

await等待的不是promise,会将其变成promise

(async()=>{
    const data = await 1//await Promise.resolve(1)
})()
(async()=>{
    isSuccess = false
    for(const name of beatyCirls){
    //开始表白
        try{
            const reply = await sendMessage(name)//表白
            console.log(reply)
            console.log('表白成功')
            isSuccess = true
            break;//退出循环
        }catch(reply){
            console.log(reply)
            console.log('表白失败')
        }
    }
    if(!isSuccess)
        console.log("注孤生")
})
(async ()=>{
    const data = await getHeros()
    const result = data.map((d)=>`<li>${d.cname}</li>`).join('')
    ul.innerHTML = result
})()

用try catch替代promise的then catch

相关面试题

执行:执行栈 | 宏队列 | 微队列 | promise状态 输出:

题目1

  • new Promise创建实例的参数回调直接执行,不进入事件队列
  • resolve()确定了promise的状态,不代表函数return,resolve之后的代码仍需执行
  • promise.then 进入微队列排队
const promise = new Promise((resolve,reject)=>{
    console.log(1)
    resolve()//直接修改了promise的状态fulfilled 数据undefined
    console.log(2)
})
promise.then(()=>{
    console.log(3)
})
console.log(4)
//1 2 4 3

题目2

  • promise进入微队列的时机,只有状态改变才会进入微队列
const promise = new Promise((resolve,reject)=>{
    console.log(1)
    setTimeout(()=>{//settimeout回调被取回执行栈的时候,promise.then才进入微队列
        console.log(2)
        resolve()//then回调进入微队列
        console.log(3)
    })
})
promise.then(()=>{
    console.log(4)
})
console.log(5)
//1 5 2 3 4

题目3

  • Promise链式调用,前面pending后面也是pending
  • 前面的状态改成了fulfilled undefine,前面的promise没有后续处理,后面promise的状态和数据继承前面的
  • 前面的状态改变了,后续执行无错,后续promise的状态为fulfilled,数据为返回值
const promise1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve()
    },1000)
})
const promise2 = promise1.catch(()=>{//此回调不执行,没有失败
    return 2
})
console.log('promise1',promise1)
console.log('promise2',promise2)
setTimeout(()=>{
    console.log('promise1',promise1)
    console.log('promise2',promise2)
},2000)
//Promise {pending} Promise {pending} 
//Promise {fulfilled undefined} Promise {fulfilled undefined}
===================================
将上述resolve改成reject()
//Promise {pending} Promise {pending} 
//Promise {rejected undefined} Promise {fulfilled 2}

题目4

  • await后面的代码是放在then,微队列里面的
async function m(){
    console.log(0)
    const n = await 1;
    console.log(n)
}
//等于
//function m(){
//    return //Promise.resolve(1).then((n)=>console.log(n))
//}
m()
console.log(2)
// 2 1

题目5

  • await 会将后面的函数变成Promise
  • async函数返回promise,await后面的代码等里面async那个promise状态改变再执行
  • await有一个async函数,await后面的代码=then
async function m(){//1. 声明
    console.log(0)
    const n = await 1 //状态fulfilled,console.log(1)放进微队列
    console.log(n)
}
(async ()=>{
    await m()//2. 执行m 
    console.log(2)
})()
console.log(3)
// 0 3 1 2

题目6

  • await 嵌套 啊~ 为什么????
  • m2没加await
  • async函数全部执行完毕,有返回结果即执行结束
  • m3 先执行,输出的前两个是内部打印的m2
  • 同步m3 执行m2执行m1,m1的promise最先声明执行并改变状态fulfilled
  • m2是async函数,返回的promise状态是pending,因为async内的函数没有执行完毕,所以m3中执行m2返回的状态是pending image.png 相关promise执行及状态改变的过程: image.png
async function m1(){
    return 1
}
async function m2(){
    const n = await m1()//await Promise.resolve(1) await 1 ,将console.log(1)放入微队列
    console.log(n)
    return 2
}
async function m3(){//里面没有await,相当于同步
    const n = m2()// 没有await 
    console.log(n)// Promise {pending}
    return 3
}
m3().then((n)=>{
    console.log(n)
})
m3()
console.log(4)

题目7

  • then方法里面传的不是回调函数,then方法返回的promise的状态和数据和前面的promise保持一致
  • 传数字,传对象都不是回调函数
  • console.log是函数,可以作为回调,then的promise状态fulfilled之后,then的回调会被调用 image.png
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)

题目8

  • 变量的赋值其实就是右边表达式执行的过程,因为赋值需要等右边的表达式执行完毕后再赋值
  • 所以b是链式then的最后一个Promise {pending}
  • a在赋值执行右边表达式的过程中打印a,得到undefined,执行完[同步走一遍]再打印会得到promise {pending}
let a //没赋值 undefined
let b = new Promise((resolve,reject)=>{
    console.log('promise1')
    setTimeout(()=>{
        resolve()
    },1000)
}).then(()=>{
    console.log('promise2')
}).then(()=>{
    console.log('promise3')
}).then(()=>{
    console.log('promise4')
})
a = new Promise(async (resolve,reject)=>{
    console.log(a)
    await b//b是最后一个then的Promise,第一个promise的resolve在宏任务里
    console.log(a) //await之后再执行右边的表达式已经执行了,Promise {p}
    console.log("after1")
    await a //我等待我完成之后再完成我,永不执行
    resolve(true)
    console.log("after2")
})
console.log("end")

image.png

题目9

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(()=>{
    console.log('setTimeout')
})
async1()
new Promise(function(resolve){
    console.log('promise1')
    resolve()
}).then(function(){
    console.log('promise2')
})
console.log('script end')

image.png

题目10

  • 创建node环境的微队列:
setTimeout(function(){
    console.log(1)
},0)
process.nextTick(function(){
    console.log(2)
})
console.log(3)
//3 2 1
  • 创建浏览器环境的微队列
  <script>
    const p = document.createElement('p')
    setTimeout(function () {
      console.log(1)
    })//默认0
    const observer = new MutationObserver(function () {
      console.log("变化了")
    })
    observer.observe(p, {
      childList: true//内部所有变化
    })
    p.innerHTML = '1'
    console.log(2)
  </script>
  //2 变化了 1
  • 创建微任务函数
function runMicroTask(callback) {
  if (process && process.nextTick) {//node环境
    process.nextTick(callback)
  } else if (MutationObserver) {//browser环境 需要优化成单例模式
    const p = document.createElement('p')
    const observer = new MutationObserver(callback)//为了把它放进微队列
    observer.observe(p, {
      childList: true
    })
    p.innerHTML = '1'//触发callback直接进入微队列
  } else {
    setTimeout(callback, 0)
  }
}
setTimeout(()=>console.log(1))
runMicroTask(()=>console.log(2))
console.log(3)

Promise实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
/**
 * 运行一个微队列 把传递的函数放到微队列
 * @param {Function} callback 
 */
function runMicroTask(callback) {
  if (process && process.nextTick) {
    process.nextTick(callback)
  } else if (MutationObserver) {//需要优化成单例模式
    const p = document.createElement('p')
    const observer = new MutationObserver(callback)//为了把它放进微队列
    observer.observe(p, {
      childList: true
    })
    p.innerHTML = '1'//触发callback直接进入微队列
  } else {
    setTimeout(callback, 0)
  }
}

class MyPromise {
  /**
   * @param {Function} executor 任务执行器,立即执行
   */
  constructor(executor) {
    this._state = PENDING
    this._value = undefined
    this._handles = [];
    try {
      executor(this._resolve.bind(this), this._reject.bind(this))//立即执行,内部抛出错误,状态改为rejected
    } catch (error) {
      this.reject(error)
    }
  }
  /**
   * 向处理队列中添加函数
   * @param {Function} executor  添加的函数
   * @param {String} state 该函数什么状态下执行
   * @param {Function} resolve 让then函数返回的Promise成功
   * @param {Function} reject 让then函数返回的Promise失败
   */
  _pushHandles(executor, state, resolve, reject) {
    this._handles.push({
      executor, state, resolve, reject
    })
  }
  _changeState(newState, value) {
    if (this._state !== PENDING) return
    this._state = newState
    this._value = value
  }
  _resolve(data) {
    this._changeState(FULFILLED, data)
  }
  _reject(reason) {
    this._changeState(REJECTED, reason)
  }
  /**
   * 
   * @param {Function} onFulfilled 
   * @param {Function} onRejected 
   * @returns 
   */
  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      this._pushHandles(onFulfilled, FULFILLED, resolve, reject)
      this._pushHandles(onRejected, REJECTED, resolve, reject)
    })
  }
}

const pro = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(123)
    console.log(pro)
  }, 0);
  reject(000)
})
pro.then(function A1(d) { console.log(d) }, function A2(a) { console.log(a) })
pro.then(function B1(d) { console.log(d) }, function B2(a) { console.log(a) })
console.log(pro)