Async 函数是一块“语法糖”

原文链接:小哥哥小姐姐,来尝尝 Async 函数这块语法糖

ES7(ECMAScript 2016)推出了Async函数(async/await),实现了以顺序、同步代码的编写方式来控制异步流程,彻底解决了困扰JavaScript开发者的“回调地狱”问题。

const result = [];
// pseudo-code, ajax stand for an asynchronous request
ajax('url1', function(err, data){
    if(err) {...}
    result.push(data)
    ajax('url2', function(err, data){
        if(err) {...}
        result.push(data)
    })
})
console.log(result)

现在可以写成如下同步代码的样式了:

async function example() {
  const r1 = await new Promise(resolve =>
    setTimeout(resolve, 500, 'slowest')
  )
  const r2 = await new Promise(resolve =>
    setTimeout(resolve, 200, 'slow')
  )
  return [r1, r2]
}

example().then(result => console.log(result))
// ['slowest', 'slow']

Async函数需要在function前面添加async关键字,同时内部以await关键字来“阻塞”异步操作,直到异步操作返回结果,然后再继续执行。

当前JavaScript编程主要是异步编程

  • 当前JavaScript编程主要是异步编程。为什么这么说呢?网页或Web开发最早从2005年Ajax流行开始,逐步向重交互时代迈进。
  • 特别是SPA(Single Page Application,单页应用)流行之后,一度有人提出“Web页面要转向Web应用,而且要媲美原生应用”。
  • 如今在前端开发组件化的背景下催生的Angular、React和Vue,都是SPA进一步演化的结果。
  • 页面在首次加载过程中,与JavaScript相关的主要任务就是加载基础运行库和扩展库(包括给低版本浏览器打补丁的脚本),然后初始化和设置页面的状态。
  • 目前JavaScript编程最大的应用是Web交互,而Web交互的核心就是异步逻辑。

ES6之前JavaScript中控制异步流程的手段只有事件和回调。比如下面的示例展示了通过原生XMLHttpRequest对象发送异步请求,然后给onloadonerror事件分别注册成功和错误处理函数:

var req = new XMLHttpRequest();
req.open('GET', url);

req.onload = function () {
    if (req.status == 200) {
        processData(req.response);
    } 
};

req.onerror = function () {
    console.log('Network Error');
};

req.send();

事件和回调有很多问题,主要是它们只适用于简单的情况。逻辑一复杂,代码的编写和维护成本就成倍上升。比如,大家熟知的“回调地狱”。更重要的是,回调模式的异步本质与人类同步、顺序的思维模式是相悖的。
为了应对越来越复杂的异步编程需求,ES6推出了解决上述问题的Promise

Promise

Promise,人们普遍的理解就是:“Promise是一个未来值的占位符”。也就是说,从语义上讲,一个Promise对象代表一个对未来值的“承诺”(promise),这个承诺将来如果“兑现”(fulfill),就会“解决”(resolve)为一个有意义的数据;如果“拒绝”(reject),就会“解决”为一个“拒绝理由”(rejection reason),就是一个错误消息。
Promise对象的状态很简单,一生下来的状态是pending(待定),将来兑现了,状态变成fulfilled;拒绝了,状态变成rejectedfulfilledrejected显然是一种“确定”(settled)状态。以上状态转换是不可逆的

Promise对象的状态.png

以下是通过Prmoise(executor)构造函数创建Promise实例的详细过程:要传入一个“执行函数”(executor),这个执行函数又接收两个参数“解决函数”(resolver)和“拒绝函数”(rejector),代码中分别对应变量resolvereject,作用分别是将新建对象的状态由pending改为fulfilledrejected,同时返回“兑现值”(fulfillment)和“拒绝理由”(rejection)。当然,resolvereject都是在异步操作的回调中调用的。调用之后,运行时环境(浏览器引擎或Node.js的libuv)中的事件循环调度机制会把与之相关的反应函数——兑现反应函数或拒绝反应函数以及相关的参数添加到“微任务”队列,以便下一次“循检”(tick)时调度到JavaScript线程去执行。

Prmoise(executor).png

如前所述,Promise对象的状态由pending变成fulfilled,就会执行“兑现反应函数”(fulfillment reaction);而变成rejected,就会执行“拒绝反应函数”(rejection reaction)。如下例所示,常规的方式是通过p.then()注册兑现函数,通过p.catch()注册拒绝函数:

p.then(res => { // 兑现反应函数
  // res === 'random success'
})
p.catch(err => { // 拒绝反应函数
  // err === 'random failure'
})

当然还有非常规的方式,而且有时候非常规方式可能更好用:

// 通过一个.then()方法同时注册兑现和拒绝函数
p.then(
  res => {
    // handle response
  },
  err => {
    // handle error
  }
)
// 通过.then()方法只注册一个函数:兑现函数
p.then(res => {
  // handle response
})
// 通过.then()方法只传入拒绝函数,兑现函数的位置传null
p.then(null, err => {
  // handle error
})

关于Promise就这样吧。ES6除了Promise,还推出了Iterator(迭代器)和Generator(生成器),于是就有成就Async函数的PIG组合。(详见原文链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • title: promise总结 总结在前 前言 下文类似 Promise#then、Promise#resolv...
    JyLie阅读 12,436评论 1 21
  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,853评论 1 56
  • 你不知道JS:异步 第三章:Promises 接上篇3-1 错误处理(Error Handling) 在异步编程中...
    purple_force阅读 1,509评论 0 2
  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,426评论 5 22
  • 引用: 供奉的遗像是牵引家人回家的通道,驻留的记忆是保持亡灵存续的神力,热闹的音乐是唤醒思念启封的药引。我为你写了...
    同小姐77阅读 349评论 0 1

友情链接更多精彩内容