async函数学习笔记
含义
async函数是什么?一句话,它就是Generator函数的语法糖。
const fs = require('fs')
const readFile = function(fileName){
return new Promise(function(resolve,reject){
fs.readFile(fileName,function(error,data){
if(error) return reject(error);
resolve(data);
})
})
}
const gen = function*(){
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}
//上面代码的函数gen可以写成async函数,就是下面这样。
const asyncReadFile = async function(){
const f1 = await readFile('/etc/fstab');
cosnt f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}
一比较就会发现,async函数就是将Generator函数的星号替换成async,将yield替换成await,仅此而已。
async函数对Generator函数的改进,体现在以下四点。
(1)内置执行器
Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
asyncReadFile()
上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像Generator函数,需要调用next方法,或则用co模块,才能真正执行,得到最后结果。
(2)更好的语义
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性
co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
(4)返回值是Promise
async函数的返回值是Promise对象,这比Generator函数的返回值Iterator对象方便多了。你可以用then方法指定下一步的操作。
进一步说,async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。
基本用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
下面是一个例子。
async function getStockPriceByName(name){
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function(result){
console.log(result);
})
上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
下面是另一个例子,指定多少毫秒后输出一个值。
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms);
})
}
async function asyncPrint(value,ms){
await timeout(ms);
console.log(value)
}
asyncPrint('hello world',50);
由于async函数返回的是Promise对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。
async function timeout(ms){
await new Promise((resolve)=>{
setTimeout(resolve,ms);
})
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 5000);
async函数有多种使用形式。
//函数声明
async function foo(){}
//函数表达式
const foo = async function(){};
//对象的方法
let obj = {async foo(){}};
obj.foo().then(...)
//Class的方法
class Storage{
constructor(){
this.cachePromise = caches.open('avatars');
}
async getAvatar(name){
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(...);
//箭头函数
const foo = async()=>{};
返回Promise对象
async函数返回一个Promise对象。
async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f(){
return 'hello world';
}
f().then(v=>console.log(v))//'hello world'
上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到。
async函数内部抛出错误,会导致返回Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f(){
throw new Error('出错了');
}
f().then(
v=>console.log(v),
e=>console.log(e)
)
//Error;出错了
Promise对象的状态变化
async函数返回的Promise对象,必须等到内部所有await命令后面的Promise对象执行完,才会发生状态改变,除非遇到return语句或则抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
上面代码中,函数getTitle内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log()
await命令
正常情况下,await命令后面是一个Promise对象,返回该对象的结果。如果不是Promsie对象,就直接返回对应的值。
async function f(){
//等同于123;
return await 123;
}
f().then(v=>console.log(v))
上面代码中,await命令的参数是数值123,这时等同于return 123.
另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于Promise对象。
class Sleep{
constructor(timeout){
this.timeout = timeout;
}
then(resolve,reject){
const startTime = Date.now();
setTimeout(
()=>resolve(Date.now()-startTime)
this.timeout
)
}
}
(async()=>{
const actualTime = await new Sleep(1000);
console.log(actualTime);
})();
上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是Promise对象,但是因为定义了then方法,await会将其视为Promise处理。
await命令后面的Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。
async function f(){
await Promise.reject('出错了');
}
f().then(v=>console.log(v)).catch(e=>console.log(e));
注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入到catch方法的回调函数。这里如果是await前面加上return,效果是一样的。
任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
async function f(){
try{
await Promise.reject('出错了');
}catch(e){
}
return await Promise.resolve('hello world');
}
f().then(v=>console.log(v))//hello world
另一种方法是await后面的Promise对象再跟一个catch方法,处理前面可能出现的错误。
async function f(){
await Promise.reject('出错了')
.catch(e=>console.log(e));
return await Promise.resolve('hello world');
}
f().then(v=>console.log(v))
//出错了
//hello world
错误处理
如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。防止出错的方法,也是将其放在try...catch代码块之中。
async function f(){
try{
await new Promise(function(resolve,reject){
throw new Error('出错了')
});
}catch(e){}
return await('hello world');
}
如果有多个await命令,可以统一放在try...catch结构中。
async function main(){
try{
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1,val2);
console.log('Final',val3);
}
catch(err){
console.error(err);
}
}
下面的例子使用try...catch结构,实现多次重复尝试。
const superagent = require('superagent');
const NUM_RETRIES = 3;
async function test(){
let i;
for(i=0;i<NUM_RETRIES;i++){
await superagent.get('http://goole.com/this-throws-an-error');
break;
}catch(err){}
console.log(i);//3
}
test();
上面代码中,如果await操作成功,就会使用break语句退出循环;如果失败,会被catch语句捕捉,然后进入下一轮循环。
来源:https://segmentfault.com/a/1190000017469670
async函数学习笔记的更多相关文章
- async 函数学习笔记
async函数就是Generator函数的语法糖. var fs = require('fs'); var readFile = function (fileName) { return new Pr ...
- async 函数--学习笔记一
含义: ES2017 标准引入了 async 函数,使得异步操作变得更加方便.async 函数是什么?一句话,它就是 Generator 函数的语法糖. 前文有一个 Generator 函数,依次读取 ...
- C# async await 学习笔记2
C# async await 学习笔记1(http://www.cnblogs.com/siso/p/3691059.html) 提到了ThreadId是一样的,突然想到在WinForm中,非UI线程 ...
- C++学习基础十六-- 函数学习笔记
C++ Primer 第七章-函数学习笔记 一步一个脚印.循序渐进的学习. 一.参数传递 每次调用函数时,都会重新创建函数所有的形参,此时所传递的实参将会初始化对应的形参. 「如果形参是非引用类型,则 ...
- C#同步,异步的理解,包括5.0中await和async(学习笔记)
之前在工作中一直用的是同步线程,就是先进入画面的load事件,然后在里面进行数据库调用的处理.后面又遇到了公司软件中一些比较古老的代码,一开始在那块古老代码中增加机能的时候,我想用到数据库的数据给画面 ...
- async/await学习笔记
async/await 的目的是简化使用 promises 的写法. 让我们来看看下面的例子: // 一个标准的 JavaScript 函数 function getNumber1() { r ...
- contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>
说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...
- Swift2.0 函数学习笔记
最近又有点忙,忙着找工作,忙着适应这个新环境.现在好了,上班两周周了,也适应过来了,又有时间安安静静的就行我们前面的学习了.今天这篇笔记,记录的就是函数的使用.下面这些代码基本上是理清楚了函数的额使用 ...
- MYSQL存储过程和函数学习笔记
学至Tarena金牌讲师,金色晨曦科技公司技术总监沙利穆课程笔记的综合. 1. 什么是存储过程和函数 将SQL语句放入一个集合里,然后直接调用存储过程和函数来执行已经定义好的SQL语句,通过存储过程和 ...
随机推荐
- [Python Cookbook] Numpy Array Joint Methods: Append, Extend & Concatenate
数组拼接方法一 思路:首先将数组转成列表,然后利用列表的拼接函数append().extend()等进行拼接处理,最后将列表转成数组. 示例1: import numpy as np a=np.arr ...
- Careercup | Chapter 5
5.1 You are given two 32-bit numbers, N andM, and two bit positions, i and j. Write a method to inse ...
- 【ZJOI2016】大♂森林
题目描述 小Y家里有一个大森林,里面有 $n$ 棵树,编号从 $1$ 到 $n$ .一开始这些树都只是树苗,只有一个节点,标号为 $1$ .这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长 ...
- [ONTAK2010]Peaks
题目大意: 一个图上有$n(n\leq100000)$个带权点,$m(m\leq500000)$条带权边.有$q(q\leq500000)$组询问,每次询问从点$v$出发,只经过权值小于等于$x$的边 ...
- Linux环境下编译JDK
环境准备 操作系统,ubuntu-14.04.6-desktop-amd64.iso,下载地址:http://59.80.44.100/releases.ubuntu.com/14.04/ubuntu ...
- IntelliJ IDEA重构技巧收集
https://segmentfault.com/a/1190000002488608(重命名文件) http://www.jianshu.com/p/ab298b46bf50(快速生成方法) htt ...
- py3处理数据库
处理mysql使用 pymysql模块 import pymysql conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',passw ...
- C++静态库与动态库深入研究
什么是库 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可以被操 ...
- 【转】Linux上的free命令详解
解释一下Linux上free命令的输出.默认输出是KB,可以用free -m则输出是MB 下面是free的运行结果,一共有4行.为了方便说明,我加上了列号.这样可以把free的输出看成一个二维数组FO ...
- 【Sprint3冲刺之前】TDzhushou软件项目测试计划书
TDzhushou软件测试计划文档 文档编号:2014-5-8 产品版本:1.1 产品名称:TDzhushou 文 档 作 者: 解凤娇 日期:2014-5-4 软件测试计划 目录 第一章 ...