当前位置: 首页 > news >正文

【学习笔记】JavaScript异步编程

一、基本概念

JavaScript代码执行:单线程

  • 优点:安全、简单
  • 缺点:耗时任务阻塞执行

JavaScript任务执行模式:

  • 同步模式(Synchronous):代码按顺序依次排队执行
  • 异步模式(Asynchronous):不会等待这个任务的结束才开始下一个任务

异步编程主要内容:

  • 同步模式和异步模式——表象差异 和 存在意义
  • JS的单线程如何实现的异步模式——实现循环 和 消息队列
  • 异步编程的几种方式
  • Promise异步编程方案、宏任务/微任务队列
  • Generator异步方案、Async/Await语法糖

所有异步编程方案的根基:回调函数(调用者调用,执行者执行)

异步编程方案:

  • 传统回调(回调地狱问题)
  • Promise对象
  • 三种状态(状态不可逆):
    • Pending
    • Rejected->onRejected
    • Fulfilled->onFulfilled
  • then
    • Promise 对象的 then 方法会返回一个全新的 Promise
    • 如果回调中返回的是 Promise,那后面 then 方法的回调会等待它的结東
    • 前面 then 方法中回调函数的返回值会作为后面 then 方法回调的参数
    • 对象后面的 then 方法就是在为上一个 then 返回的 Promise 注册回调
  • 异常处理:
    • then方法第二个参
    • catch方法:
  • promise上任何一个异常都会被一直向后传递直到捕获
  • 全局对象注册:unhandledrejection
  • 并行执行
    • Promise.all(所有成功才成功)
    • Promise.race(只会等待第一个结束的任务)

微任务 vs 宏任务

  • 微任务(临时任务):直接在当前任务结束后立即执行(Promise、MutationObserver、process.nextTick)
  • 宏任务:回调队列中的任务

ES2015:Generator(生成器函数)

  • function * handle() {} 创建生成器函数
  • yield、next控制代码执行
  • done判断是否执行完yield

ES2015:Async(语言层面的异步编程标准)

二、相关代码

同步执行

console.log('global begin')
function bar() {
  console.log('bar task')
}
function foo() {
  console.log('foo task')
  bar()
}
foo()
console.log('global end')

/*
调用栈:js在执行引擎当中维护了一个正在工作的工作表
调用栈执行步骤:
1. 在调用栈中压入一个匿名调用(可理解为把全部的代码放入一个匿名函数种执行)
2. console.log('global begin') 压入调用栈 执行并弹出
3. foo函数压入调用栈并执行
4. console.log('foo task') 压入执行并弹出
5. bar函数压入调用栈并执行
6. console.log('bar task') 压入执行并弹出
7. bar函数执行完成弹出
8. foo函数执行完成弹出
9. console.log('global end') 压入调用栈 执行并弹出
*/

异步执行

console.log('global begin')
setTimeout(function timer1(){
  console.log('timer1 invoke')
},1800)
setTimeout(function timer1(){
  console.log('timer2 invoke')
  setTimeout(function inner(){
  	console.log('inner invoke')
	},1000)
},1000)
console.log('global end')

/*
调用栈
内部API环境(例如web APIs)
事件循环(event loop):负责监听调用栈和消息队列
消息队列/回调队列

调用栈执行步骤:
1. 在调用栈中压入一个匿名调用(可理解为把全部的代码放入一个匿名函数种执行)
2. console.log('global begin') 压入调用栈 执行并弹出
3. setTimeout(timer1)压栈,在内部API环境中为timer1开启一个倒计时器,当倒计时结束让入消息队列
4. setTimeout(timer2)压栈,在内部API环境中为timer2开启一个倒计时器,当倒计时结束让入消息队列
5. console.log('global end') 压入调用栈 执行并弹出
6. 清空调用栈
7. event loop监听到调用栈为空开始从消息队列中取出第一个timer2回调函数压栈
8. console.log('timer2 invoke') 压入调用栈 执行并弹出
9. setTimeout(inner)压栈,在内部API环境中为inner开启一个倒计时器,当倒计时结束让入消息队列
10. event loop消息队列中取出timer1回调函数压栈
11. console.log('timer1 invoke') 压入调用栈 执行并弹出
12. timer1执行完毕弹出
13. event loop消息队列中取出inner回调函数压栈
14. console.log('inner invoke') 压入调用栈 执行并弹出
15. inner执行完毕弹出
*/

回调函数

function foo(callback) {
  setTimeout(function () {
    callback()
  },3000)
}
foo(function() {
  console.log('这就是一个回调函数')
})

Promise 基本示例

const promise = new Promise(function (resolve, reject) {
  // 这里用于“兑现承诺”
  resolve(100) // 承诺达成 状态不可逆
  // reject(new Error('promise rejected')) // 承诺失败
})
promise.then(function(value) {
  console.log('resolved', value)
}, function(error) {
  console.log('rejected', error)
})

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('xxxx').then(function(res) {
  console.log(res)
}, function(error) {
  console.log(error)
})

Promise链式调用

ajax('xxxx')
  .then(function(res) {
    console.log('111')
    return ajax('sssss')
	})
 .then(function(res) {
  	console.log('222')
    console.log(res) // ssss接口请求的返回
	})
 .then(function(res) {
  	console.log('333')
	})

// 每一个then方法实际上都是在为上一个then返回的promise对象添加状态明确过后的回调

Promise异常处理

// then捕获
ajax('xxxx')
  .then(function onFulfilled(res) {
    console.log('onFulfilled', res)
	}, function onRejected(error) {
    console.log('onRejected', res)
  })

// catch捕获
ajax('xxxx')
  .then(function onFulfilled(res) {
    console.log('onFulfilled', res)
  })
	.catch(function onRejected(error) {
    console.log('onRejected', error)
  })

// catch捕获等同于then如下写法捕获错误
ajax('xxxx')
  .then(function onFulfilled(res) {
    console.log('onFulfilled', res)
  })
  .then(undefined, function onRejected(error) {
    console.log('onRejected', error)
  })

全局异常捕获

window.addEventListener('undandledrejection', event => {
  const { reason, promise } = event
  console.log(reason, promise)

  event.preventDefault
},false)

// node
process.on('undandledrejection', (reason, promise) => {
  console.log(reason, promise)
  // reason:Promise 失败原因,一般是一个错误对象 
  // promise:出现异常的 Promise 对象
})

Promise并行执行

var promise = Promise.all([ajax('xxx'),ajax('ssss')])
promise.then(function(values) {
  console.log(values)
}).catch(function(error) {
  console.log(error)
})

const request = ajax('wqeqweqw')
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)
})

Generator(生成器函数)

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.next('bar')
generator.throw(new Error('Generator error'))

Generator配合Promise

function * main() {
  try {
    const users = yield ajax('/api/user.json')
    console.log(users)
    const posts = yield ajax('/api/posts.json')
    console.log(posts)
  } catch(e) {
    console.log(e)
  }
}

function co (generator) {
  const g = generator()
  const result = g.next()
  
  function handleResult(result) {
    if(result.done) return 
    result.value.then(data => {
      handleResult(g.next(data))
    }, error => {
      g.throw(error)
    })
  }
  handleResult(g.next())
}

co(main)

Async函数

async function main() {
  try {
    const users = await ajax('/api/user.json')
    console.log(users)
    const posts = await ajax('/api/posts.json')
    console.log(posts)
  } catch(e) {
    console.log(e)
  }
}
const promise = main()
promise.then(() => {
  console.log('all completed')
}) 

相关文章:

  • Java Number Math 类
  • 【Go实现】实践GoF的23种设计模式:命令模式
  • ETCD的创建
  • 沥青瓦UKCA认证—EN 544
  • 一本通 1276:【例9.20】编辑距离
  • c语言复习之文件(十三)
  • 【OJ每日一练】1033 - 等级成绩
  • 5G URLLC标准化关键技术分析
  • 安装GitHub上一些库的注意事项
  • 知识蒸馏DEiT算法实战:使用RegNet蒸馏DEiT模型
  • 《MySQL高级篇》九、数据库的设计规范
  • 我,30多岁的土木工程人,终于转行了
  • 动态DNS与DPDK高性能DNS -DPDK环境搭建
  • 【Numpy基础知识】通用函数ufunc基础知识
  • 甜点cc的2022年回顾总结
  • 【软件测试】瓶颈?资深测试聊测试开发的瓶颈在哪?
  • 基于SPRINGBOOT的高校羽毛球馆信息管理系统的设计与实现
  • NLP | 文本预处理
  • CSS -- 使用纯CSS实现旋转木马相册的效果
  • Fabric.js 限制边框宽度缩放