【学习笔记】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')
}) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值