JavaScript异步编程

内容概要

  • 同步模式与异步模式
  • 事件循环与消息队列
  • 异步编程的几种方式
  • Promise异步方案、宏任务/ 微任务队列
  • Generator异步方案、Async/ Await 语法糖

单线程模式

JS执行环境中负责执行代码的线程只有一个
原因:
JavaScript 采用单线程模式工作, 最早JavaScript 是一门运行在浏览器端的脚本语言, 目的是为了实现页面的动态交互, 实现交互的核心是DOM操作, DOM操作决定了JavaScript必须以单线程模式运行, 否则会出现很复杂的线程同步问题
优点:
更安全 更简单
缺点:
如果遇到一个特别耗时的任务, 后面的任务要排队等待任务的结束, 导致整个程序的执行会被拖延, 出现假死的情况
解决:
将任务执行模式分为: 同步模式 和 异步模式

同步模式(Synchronous)

同步模式就是指代码中的任务依次执行, 后一个任务必须等前一个任务执行结束才能执行。程序的执行顺序和代码的编写顺序完全一致。

异步模式(Asynchronous)

异步模式的API不会等待这个任务的结束才开始下一个任务, 对于耗时操作, 开启过后就立即往后执行下一个任务, 后续逻辑一般通过回调函数的方式定义
优点: 使得单线程的JavaScript语言可以同时处理大量耗时任务
缺点: 代码的执行顺序混乱

回调函数(所有异步编程方案的根基)

由调用者定义, 交给执行者执行的函数就被称为回调函数

function foo(callback){
  setTimeout(function(){
    callback()
  }, 3000)
}
foo(function(){
  console.log('这就是一个回调函数')
  console.log('调用者定义这个函数,执行者执行这个函数')
  console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
})

Promise(一种更优的异步编程统一方案)

直接使用传统回调方式去完成复杂的异步流程, 无法避免大量的函数嵌套, 容易形成回调地狱, CommonJS社区提出了Promise的规范, 在ES2015中被标准化, 称为语言规范

在这里插入图片描述

//Promise 基本示例
const promise = new Promise(function(resolve, reject){
  //这里用于"兑现"承诺
  resolve(100) //承诺达成 
  //reject(new Error('promise rejected')) //承诺失败
})
//注意:即便promise中没有任何异步操作, then方法后指定的回调函数仍会进入回调队列排队
promise.then(function(value){
  console.log('resolved',value)
},function(error){
  comsole.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('/api/foo.json').then(res=>{},err=>{})

借用 Promise then 方法链式调用的特点, 尽可能保证异步任务的扁平化

  • Promise 对象的 then 方法会返回一个全新的 Promise 对象
  • 后面的 then 方法就是在为上一个 then 返回的 Promise 注册回调
  • 前面的 then 方法中回调函数的返回值会作为后面 then 方法回调的参数
  • 如果回调中返回的是 Prormise, 那后面 then 方法的回调会等待它的结束

catch与then中失败回调的差别: catch是给整个promise链注册的失败回调, promise链条上任何一个失败异常会一直向后传递, 直至被捕获, then中失败只能捕获上一个promise的异常

//全局对象上注册unhandledrejection事件,去处理代码中没有被手动捕获的异常
window.addEventListener('unhandledrejection', event =>{
   const { reason, promise } = event
   console.log(reason, promise)
   // reason => Promise 失败原因,一般是一个错误对象
   // promise => 出现异常的 Promise 对象
   
   event.preventDefault()
}, false)

//在全局捕获不推荐, 应该在代码中明确每一个可能捕获的异常
process.on('unhandledRejection', (reason, promise)=>{
   console.log(reason, promise)
   // reason => Promise 失败原因,一般是一个错误对象
   // promise => 出现异常的 Promise 对象
})

promise静态对象

通过promise.resolve包装一个promise对象, 得到的是原本的promise

var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) //true

promise并行执行

Promise.all() 等待所有任务运行结束

Promise.all([
ajax('/api/users.json'),
ajax('/api/posts.json')
]).then(values=>{
//values是一个数组, 返回的是每一个异步返回的结果
}).catch(err=>{
//err 只要有一个失败就会被捕捉
})
ajax('/api/urls.json')
.then(value=>{
  //把所有的接口封装在promise.all中
  const urls = Object.values(value)
  const tasks = url.map(url => ajax(url))
  return Promise.all(tasks)
})
.then(values=>{
  console.log(values)
})

Promise.race() 只会等待第一个结束的任务

const request = ajax('api/posts.json')
const timeout = new Promise((resolve, reject) =>{
  setTimeout(() => reject(new Error('timeout')), 500)
})
Promise.race([
  request,
  timeout 
])
.then(value=>{
  console.log(value)
})
.catch(err=>{
  console.log(err)
})

Promise 执行时序

回调队列中的任务称之为 宏任务, 宏任务执行过程中临时加上一些临时需求, 可以选择作为一个新的宏任务进到队列中排队
微任务: 直接在当前任务结束过后立即执行,提高整体的响应能力, Promise 的回调会作为微任务执行

//微任务
console.log('global start')
setTimeout((=>{
  console.log('setTimeout')
},0)
Promise.resolve()
 .then(()=>{
 console.log('promise')
})
console.log('global end')
//global start
//global end
//promise
//promise2
//promise3
//setTimeout

目前绝大多数异步调用都是作为宏任务执行, Promise & MutationObserver & processnextTick 都是微任务

Generator 异步方案

function *foo(){
  console.log('start')
  try {
    const res = yield 'foo' //可以用yield向外返回值
    console.log(res) //bar
  }catch(err){
    console.log(err)
  }
}
const generator = foo() //得到一个生成器对象
const result = generator.next() //开始执行
console.log(result) //拿到返回值 {value:"foo", done:false} done表示是否执行完了

generator.next('bar') //像yield的左边传参
generator.throw(new Error('Generator error')) //向生成器内部抛出异常
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值