【转】Async 函数是一块“语法糖”

2018-09-12  本文已影响69人  alanwhy

原文链接:小哥哥小姐姐,来尝尝 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编程主要是异步编程

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组合。(详见原文链接

上一篇 下一篇

猜你喜欢

热点阅读