《javascript高级程序设计》学习笔记|《javascript高级程序设计》学习笔记 | 11.1.异步编程

关注前端小讴,阅读更多原创技术文章
异步编程
  • ES6 新增了正式的Promise引用类型,支持更优雅地定义和组织异步逻辑
  • 接下来的几个版本,使用asyncawait关键字定义异步函数的机制
【《javascript高级程序设计》学习笔记|《javascript高级程序设计》学习笔记 | 11.1.异步编程】相关代码 →
同步与异步
  • 同步行为在内存中顺序执行处理器指令
    • 每条指令都在单个线程中按出现顺序执行
    • 每条指令执行后,都可以推断出程序的状态,并立即获得存储在系统本地(或寄存器或系统内存)的信息
let x = 3 // 操作系统在栈内存上分配一个存储浮点数值的空间 x = x + 4 // 针对这个值做一次数学计算,并把计算结果写回之前分配的内存中

  • 异步行为类似于系统中断
    • 当前进程外部的实体可以触发代码执行,通常在定时回调中执行
    • 执行线程不知道何时将信息存储到系统本地(或寄存器或系统内存),取决于回调合适从消息队列出列并执行
let x2 = 3 // 操作系统在栈内存上分配一个存储浮点数值的空间 setTimeout(() => { x = x + 4 // 执行线程不知道x值何时会改变,取决于回调何时从消息队列出列并执行 }, 1000)

以往的异步编程方式
  • 早期的 JS 只支持回调函数表明异步操作,串联多个异步操作需深度嵌套回调函数(回调地狱)
function double(value) { setTimeout(() => { setTimeout(() => { console.log(value * 2) }, 2000) // 2000毫秒后,JS运行时会把回调函数推到消息队列上等待执行 }, 1000) // 1000毫秒后,JS运行时会把回调函数推到消息队列上等待执行 } double(3) // 6(约3000毫秒后),double()函数在setTimeout成功调度异步操作后,立即退出

异步返回值
  • setTimeout操作返回有用的值,可给异步操作提供一个回调,把这个值传给需要它的地方
function double2(value, callback) { setTimeout(() => { callback(value * 2) // 1000毫秒后,把回调函数推到消息队列上 }, 1000) } double2(3, (x) => console.log(`I was given: ${x}`)) // 'I was given: 6'(约1000毫秒后)

失败处理
  • 在回调模型中做异步操作的失败处理(成功回调 & 失败回调)
  • 该方法已不可取,因为必须在初始化异步操作时定义回调,异步函数返回值只在短时间内存在,必须预备好将该返回值作为参数的回调才能接收到它
function double3(value, success, failure) { setTimeout(() => { // 必须在初始化异步操作时定义回调 try { if (typeof value !== 'number') { throw 'Must provide number as first argument' } success(value * 2) } catch (error) { failure(error) } }, 1000) } const successCallback = (x) => console.log(`Success: ${x}`) const failureCallback = (e) => console.log(`Failure: ${e}`) double3(3, successCallback, failureCallback) // 'Success: 6'(约1000毫秒后) double3('3', successCallback, failureCallback) // 'Failure: Must provide number as first argument'(约1000毫秒后)

嵌套异步回调
  • 若异步返回值依赖另一个异步返回值,则需要嵌套回调(回调地狱,不具有扩展性,代码难以维护)
function double4(value, success, failure) { setTimeout(() => { try { if (typeof value !== 'number') { throw 'Must provide number as first argument' } success(value * 2) } catch (error) { failure(error) } }, 1000) } const successCallback2 = (x) => { double4(x, (y) => console.log(`Success: ${y}`)) // 异步返回值依赖另一个异步返回值,嵌套回调产生“回调地狱” } const failureCallback2 = (e) => console.log(`Failure: ${e}`) double4(3, successCallback2, failureCallback2) // 'Success: 12'(约2000毫秒后)

总结 & 问点
  • 在执行线程和内存存储方面,同步行为和异步行为有哪些区别?
  • 写一段代码,将 setTimeout 的返回值作为回调传给函数,并执行这个函数获取结果
  • 写一段代码,在回调模型中做异步操作的成功/失败处理,并说说为什么该方法已经不可取
  • 回调地狱是如何形成的?其有什么缺点?

    推荐阅读