欢迎留言讨论
chrome
async
函数是 Generator
函数的语法糖。使用 关键字 async
来表示,在函数内部使用 await
来表示异步。相较于 Generator
,async
函数的改进在于下面四点:编程
内置执行器。Generator
函数的执行必须依靠执行器,而 async
函数自带执行器,调用方式跟普通函数的调用同样segmentfault
更好的语义。async
和 await
相较于 *
和 yield
更加语义化promise
更广的适用性。co
模块约定,yield
命令后面只能是 Thunk 函数或 Promise对象。而 async
函数的 await
命令后面则能够是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操做)浏览器
返回值是 Promise。async
函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,能够直接使用 then()
方法进行调用bash
此处总结参考自:理解async/awaitapp
async
是ES7新出的特性,代表当前函数是异步函数,不会阻塞线程致使后续代码中止运行。异步
申明以后就能够进行调用了async
async function asyncFn() {
return 'hello world';
}
asyncFn();复制代码
这样就表示这是异步函数,返回的结果函数
async 表示函数里有异步操做
await 表示紧跟在后面的表达式须要等待结果。
返回的是一个promise
对象,状态为resolved
,参数是return
的值。那再看下面这个函数
async function asyncFn() {
return '我后执行'
}
asyncFn().then(result => {
console.log(result);
})
console.log('我先执行');复制代码
上面的执行结果是先打印出'我先执行'
,虽然是上面asyncFn()
先执行,可是已经被定义异步函数了,不会影响后续函数的执行。
如今理解了async
基本的使用,那还有什么特性呢?
async
定义的函数内部会默认返回一个promise
对象,若是函数内部抛出异常或者是返回reject
,都会使函数的promise
状态为失败reject
。
async function e() {
throw new Error('has Error');
}
e().then(success => console.log('成功', success))
.catch(error => console.log('失败', error));复制代码
咱们看到函数内部抛出了一个异常
,返回reject
,async
函数接收到以后,断定执行失败进入catch
,该返回的错误打印了出来。
async function throwStatus() {
return '能够返回全部类型的值'
}
throwStatus().then(success => console.log('成功', success))
.catch(error => console.log('失败', error));复制代码
//打印结果
成功 能够返回全部类型的值
复制代码
async
函数接收到返回的值,发现不是异常
或者reject
,则断定成功,这里能够return
各类数据类型的值,false
,NaN
,undefined
...总之,都是resolve
可是返回以下结果会使async
函数断定失败reject
throw new Error
或者返回reject
状态return Promise.reject('执行失败')
还有一点,在async
里,必需要将结果return
回来,否则的话不论是执行reject
仍是resolved
的值都为undefine
,建议使用箭头函数。
其他返回结果都是断定resolved
成功执行。
//正确reject方法。必须将reject状态return出去。
async function PromiseError() {
return Promise.reject('has Promise Error');
}
//这是错误的作法,而且断定resolve,返回值为undefined,而且Uncaught报错
async function PromiseError() {
Promise.reject('这是错误的作法');
}
PromiseError().then(success => console.log('成功', success))
.catch(error => console.log('失败', error));复制代码
咱们看到第二行多了个Promise
对象打印,不用在乎,这个是在Chrome
控制台的默认行为,咱们日常在控制台进行赋值也是一样的效果。若是最后执行语句
或者表达式
没有return
返回值,默认undefined
,作个小实验。
var a = 1;
//undefined
------------------------------------------------------------
console.log(a);
//1
//undefined
------------------------------------------------------------
function a(){ console.log(1) }
a();
//1
//undefined
------------------------------------------------------------
function b(){ return console.log(1) }
b();
//1
//undefined
------------------------------------------------------------
function c(){ return 1}
c();
//1
------------------------------------------------------------
async function d(){
'这个值接收不到'
}
d().then(success => console.log('成功',success));
//成功 undefined
//Promise { <resolved>: undefined }
-----------------------------------------------------------
async function e(){
return '接收到了'
}
e().then(success => console.log('成功',success));
//成功 接收到了
//Promise { <resolved>: undefined }复制代码
最后一行Promise { <resolved> : undefined }
是由于返回的是console.log
执行语句,没有返回值。
d().then(success => console.log('成功',success)}
等同于
d().then(function(success){
return console.log('成功',success);
});
复制代码
js自己是单线程的,经过v8咱们能够拥有"异步"的能力
认识完了async,来说讲await。
await
意思是async wait(异步等待)。这个关键字只能在使用async
定义的函数里面使用。任何async
函数都会默认返回promise
,而且这个promise
解析的值都将会是这个函数的返回值,而async
函数必须等到内部全部的 await
命令的 Promise
对象执行完,才会发生状态改变。
打个比方,await是学生,async是校车,必须等人齐了再开车。
await
函数执行完毕后,才会告诉
promise
我成功了仍是失败了,执行
then
或者
catch
async function awaitReturn() {
return await 1
};
awaitReturn().then(success => console.log('成功', success))
.catch(error => console.log('失败',error))复制代码
在这个函数里,有一个await
函数,async会等到await 1
这一步执行完了才会返回promise
状态,毫无疑问,断定resolved
。
不少人觉得await
会一直等待以后的表达式执行完以后才会继续执行后面的代码,实际上await
是一个让出线程的标志。await
后面的函数会先执行一遍(好比await Fn()的Fn ,并不是是下一行代码),而后就会跳出整个async
函数来执行后面js栈的代码。等本轮事件循环执行完了以后又会跳回到async
函数中等待await后面表达式的返回值,若是返回值为非promise
则继续执行async
函数后面的代码,不然将返回的promise
放入Promise
队列(Promise的Job Queue)
来看个简单点的例子
const timeoutFn = function(timeout){
return new Promise(function(resolve){
return setTimeout(resolve, timeout);
});
}
async function fn(){
await timeoutFn(1000);
await timeoutFn(2000);
return '完成';
}
fn().then(success => console.log(success));复制代码
这里本能够用箭头函数写方便点,可是为了便于阅读本质,仍是换成了ES5写法,上面执行函数内全部的await函数才会返回状态,结果是执行完毕3秒后才会弹出'完成
'。
正常状况下,await 命令后面跟着的是 Promise ,若是不是的话,也会被转换成一个 当即 resolve 的 Promise。
也能够这么写
function timeout(time){
return new Promise(function(resolve){
return setTimeout(function(){
return resolve(time + 200)
},time);
})
}
function first(time){
console.log('第一次延迟了' + time );
return timeout(time);
}
function second(time){
console.log('第二次延迟了' + time );
return timeout(time);
}
function third(time){
console.log('第三次延迟了' + time );
return timeout(time);
}
function start(){
console.log('START');
const time1 = 500;
first(time1).then(time2 => second(time2) )
.then(time3 => third(time3) )
.then(res => {
console.log('最后一次延迟' + res );
console.timeEnd('END');
})
};
start();复制代码
这样用then链式回调的方式执行resolve
//打印结果
START
第一次延迟了500
第二次延迟了700
第三次延迟了900
最后一次延迟1100
END
复制代码
用async/await呢?
async function start() {
console.log('START');
const time1 = 500;
const time2 = await first(time1);
const time3 = await second(time2);
const res = await third(time3);
console.log(`最后一次延迟${res}`);
console.log('END');
}
start();复制代码
达到了相同的效果。可是这样遇到一个问题,若是await
执行遇到报错呢
async function start() {
console.log('START');
const time1 = 500;
const time2 = await first(time1);
const time3 = await Promise.reject(time2);
const res = await third(time3);
console.log(`最后一次延迟${res}`);
console.log('END');
}
start();复制代码
返回reject后,后面的代码都没有执行了,以此迁出一个例子:
let last;
async function throwError() {
await Promise.reject('error');
last = await '没有执行';
}
throwError().then(success => console.log('成功', last))
.catch(error => console.log('失败',last))复制代码
其实
async
函数不难,难在错处理上。
上面函数,执行的到await
排除一个错误后,就中止往下执行,致使last
没有赋值报错。
async
里若是有多个await函数的时候,若是其中任一一个抛出异常或者报错了,都会致使函数中止执行,直接
reject
;
怎么处理呢,能够用try/catch
,遇到函数的时候,能够将错误抛出,而且继续往下执行。
let last;
async function throwError() {
try{
await Promise.reject('error');
last = await '没有执行';
}catch(error){
console.log('has Error stop');
}
}
throwError().then(success => console.log('成功', last))
.catch(error => console.log('失败',last))复制代码
这样的话,就能够继续往下执行了。
来个🌰练习下
function testSometing() {
console.log("testSomething");
return "return testSomething";
}
async function testAsync() {
console.log("testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const testFn1 = await testSometing();
console.log(testFn1);
const testFn2 = await testAsync();
console.log(testFn2);
console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> {
console.log("promise START...");
resolve("promise RESOLVE");
});
promiseFn.then((val)=> console.log(val));
console.log("===END===")复制代码
执行结果
咱们一步步来解析
首先test()
打印出test start...
而后 testFn1 = await testSomething();
的时候,会先执行testSometing()
这个函数打印出“testSometing
”的字符串。
以后由于await
会让出线程就会去执行后面的。testAsync()
执行完毕返回resolve
,触发promiseFn
打印出“promise START...
”。
接下来会把返回的Promiseresolve("promise RESOLVE")
放入Promise队列(Promise的Job Queue),继续执行打印“===END===
”。
等本轮事件循环执行结束后,又会跳回到async
函数中(test()
函数),等待以前await
后面表达式的返回值,由于testSometing()
不是async
函数,因此返回的是一个字符串“return
testSometing
”。
test()
函数继续执行,执行到testFn2()
,再次跳出test()
函数,打印出“testAsync
”,此时事件循环就到了Promise的队列,执行promiseFn.then((val)=> console.log(val));
打印出“promise RESOLVE
”。
以后和前面同样 又跳回到test函数继续执行console.log(testFn2)
的返回值,打印出“hello async
”。
最后打印“test end...
”。
加点料,让testSomething()
变成async
async function testSometing() {
console.log("testSomething");
return "return testSomething";
}
async function testAsync() {
console.log("testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const testFn1 = await testSometing();
console.log(testFn1);
const testFn2 = await testAsync();
console.log(testFn2);
console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> {
console.log("promise START...");
resolve("promise RESOLVE");
});
promiseFn.then((val)=> console.log(val));
console.log("===END===")复制代码
执行结果
promiseFn.then((val)=> console.log(val));
先于console.log(testFn1)
执行。
缘由是由于如今的版本async函数会被await resolve,
function testSometing() {
console.log("testSomething");
return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object Function]
console.log(Object.prototype.toString.call(testSometing())) // [object String]
async function testSometing() {
console.log("testSomething");
return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object AsyncFunction]
console.log(Object.prototype.toString.call(testSometing())) // [object Promise]复制代码
testSomething()
已是async函数,返回的是一个Promise对象须要等它 resolve 后将当前Promise 推入队列,随后先清空调用栈,因此会"跳出" test() 函数执行后续代码,随后才开始执行该 Promise。
今天是2019-11-06 12:03:46,收到几位读者的反馈,最后一个例子的执行时机不符合现有版本的result,缘由多是在文章完结后至今的一年半内 Chrome V8 更新致使,若是有 Opear 或者是其余使用了 Chrome 内核的浏览器,或者到 这个地址 下载旧版本 Chrome,笔者测试使用的是 Opera,在 Chrome V8 release 文档或里并未找到相关说明。
在当前的版本,async 函数不须要等待被 await resolve了,由于正常状况下它们最终返回都终将是Promise。
而现版本 await AsyncFunction 返回 Promise 的优先级相似同步任务,高于普通 Promise 的 promiseFullfil 的优先级 `promiseFn.then((val)=> console.log(val));` 有兴趣的能够看下这篇关于解析V8 的文章。
愈来愈多的人正在研究听说是异步终极编程解决方案的async/await,可是大部分人对这个方法内部怎么执行的还不是很了解,整理了await以后js的执行顺序,但愿对大家有所帮助