Promise和async/await
1、promise对象
promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。
new Promise((resolve, reject) => {
if() {
resolve();
}else {
reject();
}
});
const promise = new Promise(function(resolve, reject) {
if (){
resolve(value);
} else {
reject(error);
}
});
promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve 函数将 promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)可以将数据作为参数传递出去。reject 函数的作用是,将 promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),也可以将某些错误信息作为参数传递出去。
由于Promise 新建后会立即执行,所以可以在 promise 外面再包裹一层函数:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
} timeout(100).then((value) => {
console.log(value);
});
请注意:在 promise 构造函数中如果不 resolve 或者 reject 改变 promise 对象的状态的话,后面的 then 是不会执行的。但是如果在 then 中不改变状态,后面的链式 then 仍会执行。
//下面的then里面的程序不会执行
new Promise( (resolve, reject) => {
console.log('promise');
}).then( ()=>{
console.log('resolve');
}, ()=>{
console.log('reject');
}) //下面的第一个和第二then里面的程序都会执行
new Promise( (resolve, reject) => {
console.log('promise');
resolve();
}).then( ()=>{
console.log('then1');
}).then( ()=>{
console.log('then2');
})
1.1、then() 方法
Promise 实例是一个对象,不是一个函数。promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
new Promise().then(() => {}, () => {});
let promise = new Promise(function(resolve, reject) {
if (){
resolve(value);
} else {
reject(error);
}
}); promise.then(function(value) {
console.log(value)
}, function(error) {
console.log(error)
});
then 方法可以接受两个回调函数作为参数。第一个回调函数是 promise 对象的状态变为 resolved 时调用,第二个回调函数是 promise 对象的状态变为 rejected 时调用,第二个函数是可选的,不一定要提供。这两个函数都接受 promise 对象传出的值作为参数。
1.2、then() 方法的链式写法
then 方法返回的是一个新的 promise 实例,因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
1.2.1、then 方法里面返回一个确定值时
在一个 then() 方法里面你可以 return 一个确定的“值”,此时 then 会将这个确切的值传入一个默认的新的 Promise 实例,并且这个 Promise 实例会立即置为 fulfilled 状态,将 return 的值作为 resolve 方法的参数传递出去,以供接下来的 then 方法里使用。
let p1 = new Promise((resolve,reject) => {
resolve('aaa')
})
p1.then((data) => {
data = data + 'bbb'
return data // 此时data会作为resolve的参数传递出去
}).then((val) => {
console.log(val + ' sucess');
},(err) => {
console.log(err + ' error');
}) //输出: aaabbb sucess
1.2.1、then 方法里面返回一个 promise 实例
如果 then 方法里面返回的还是一个 promise 对象,这时后一个回调函数,就会等待该 promise 对象的状态发生变化,才会被调用。
//第一个异步任务
function a(){
return new Promise(function(resolve, reject){
resolve("a函数");
});
}
//第二个异步任务
function b(data_a){
return new Promise(function(resolve, reject){
console.log(data_a);
resolve("b函数");
});
} //连续调用
a().then(function(data){
return b(data); // 此时then方法里面返回的是一个promise对象,后面的then会等待该promise对象的状态发生改变才会被调用
}).then((data) => {
console.log(data + 'sucess')
}, (err) => {
console.log(err + 'rejected')
})
//输出:a函数 b函数sucess
上面的最后一个 then 函数等待前面的 then 函数里面的 promise 对象状态发生改变,如果变为 resolved ,就调用第一个回调函数,如果状态变为 rejected,就调用第二个回调函数。
1.2.1、then 方法里面不返回
如果 then 方法不返回数据,那么后面的 then 将无法获取到前面的数据,但是后面的 then 方法仍能执行。
//第一个异步任务
function a(){
return new Promise(function(resolve, reject){
resolve("a函数");
});
}
//第二个异步任务
function b(data_a){
return new Promise(function(resolve, reject){
console.log(data_a);
resolve("b函数");
});
} a().then(function(data){
console.log(data) //不返回
}).then((data) => {
console.log(data + ' sucess')
}, (err) => {
console.log(err + ' rejected')
})
//输出: a函数 undefined sucess
1.3、catch() 方法
Promise.prototype.catch
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
p.then((val) => console.log(val))
.then(null, (err) => console.log(err));
// 相当于
p.then((val) => console.log(val))
.catch((err) => console.log(err));
catch 方法中断调用链:
在很多情况下,如果连续的几个异步任务,其中某个异步任务处理失败,那么接下来的几个任务很大程度上就不需要继续处理了,我们可以使用 catch 方法来终止then的调用链。
function a() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject("error");
}, 1000);
});
} //这样做不会中断
//下面输出:222 333
a().then(function (data) {
console.log(111);
return data
}, function (data) { //如果是这样处理rejected状态,并不会中断调用链
console.log(222);
return data;
}).then(function (data) {
console.log(333);
}) //在调用链的末尾加上catch方法,当某个环节的Promise的异步处理出错时,将中断其后的调用,直接跳到最后的catch
//下面直接输出: 'error'
a().then(function (data) {
console.log(111);
return data
}).then(function (data) {
console.log(222);
}).catch(function (e) {
//rejected的状态将直接跳到catch里,剩下的调用不会再继续
console.log(e);
});
使用 catch 方法时,如果前面的函数里面有 reject 或者函数里面有错误的话,就会被 catch 方法捕获,立即跳转到 catch 方法里执行。前面的回调函数中,不管是运行中有错误,或者是执行了 reject ,都会立即被 catch
方法捕获。
使用 catch 捕获了错误,catch 后面的代码能继续正常执行。
new Promise().then().catch(() => {
console.log('catch 错误')
});
console.log(111); //这里的 111 仍能正常输出
2、async/await 方法
async 方法里面有 await 命令,await 命令后面跟着异步操作(可以是请求接口或者其他异步操作等),这时 async 函数会停下来,等待 await 命令后面的异步操作执行完毕,将结果返回,然后再继续执行下去。如果后面再遇到 await 命令仍是如此,所以,async 函数里面可以看做是同步操作,语句都是一行一行地依次执行的,语句执行顺序非常清晰。
2.1、async 函数的定义形式
// 函数声明
async function foo() {}
const foo = async function () {}; // 对象的方法
let obj = { async foo() {} }; // 箭头函数
const foo = async () => {}; // Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
2.2、async 函数里面的 await 命令
注意,await 命令后面只能是 Promise 对象或者原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。只有后面的 promise 对象返回了值 await 命令才算执行完毕,程序才会继续执行。
2.2.1、await 跟着 promise 对象
一般来说,await
命令后面应该跟着一个 Promise 对象,如果该 promise 对象触发的是 resolve 方法,那么 await 就会接收到 resolve 方法的参数,并将其返回。
function timeout() {
return new Promise((resolve) => {
setTimeout(function (){
resolve('aaa')
},1000);
});
}
async function asyncPrint() {
console.log(await this.timeout()); //await会返回resolve的参数
console.log(111);
}
asyncPrint(); //一秒钟过后按顺序执行,依次输出 aaa 111
如果触发的是 reject 方法,await 无法接收到 reject 返回的值,此时必须用 catch 方法来捕获这个错误,否则会报错。
(1)可以用 try...catch 来捕获异常
注意,在 try...catch 里面捕获异步操作的异常,必须使用 await 同步写法,否则会捕获不到。
async created () {
async function f() {
await Promise.reject("出错了!!");
}
try {
await f(); //注意,这里必须使用同步(await )的写法,否则捕获不到错误
} catch (error) {
console.log(error); //输出 出错了!!
}
}
(2)也可以在调用的时候直接在后面加上 catch 来捕获异常。
下面代码中,await
语句前面没有return
,但是reject
方法的参数依然传入了catch
方法的回调函数。这里如果在await
前面加上return
,效果是一样的。
async function f() {
await Promise.reject('出错了!!');
} f().then(v => console.log(v)).catch(e => console.log(e)) //输出: 出错了!!
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(function() {
reject("aaa");
}, 1000);
});
}
timeout().then(val => {
console.log(val);
}).catch(err => {
console.log(err); //捕获到异常 输出 aaa
})
reject 的参数会被后面第一个出现的 catch 方法捕获到。
任何一个await
语句后面的 Promise 对象变为reject
状态,那么整个async
函数都会中断执行。
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await
放在try...catch
结构里面,这样不管这个异步操作是否成功,第二个await
都会执行。
async function f() {
try {
await Promise.reject('出错了'); //使用await写法
} catch(e) {
console.log(e);
}
return await Promise.resolve('helloworld');
} f().then(v => console.log(v)) //输出: helloworld
2.2.2、await 跟着原始类型的值
await 命令后面只能是 Promise 对象或者原始类型的值,如果 await 命令后面是原始类型的值,则直接返回对应的值。
async function f() {
// 等同于 return 123;
let a = await 123;
console.log(a)
}
f(); //输出: 123
2.2.3、await 后面跟其他类型的值将没有效果
function timeout() {
setTimeout(() => {
console.log('aaa');
}, 1000);
}
async function asyncPrint() {
await timeout(); //此时的await不起作用
console.log(111);
}
asyncPrint(); //先输出111,后输出aaa,并不能起到顺序执行的作用
2.3、async 函数返回的值是一个 promise 对象
async
函数返回的是一个 Promise 对象,可以使用then
方法添加回调函数。
async
函数内部return
语句返回的值,会成为then
方法回调函数的参数。
async function f() {
return 'hello world';
} f().then(v => console.log(v)) // "hello world"
async
函数返回的 Promise 对象,必须等到内部所有await
命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return
语句或者抛出错误。也就是说,只有等到async
函数内部的await 后面的异步操作执行全部完,promise 对象状态才会发生改变,然后才能执行后面添加的then
方法指定的回调函数。
let p1 = new Promise((resolve,reject)=>{
resolve('aaa')
})
let p2 = new Promise((resolve,reject)=>{
resolve('aaa')
})
async function getUrl(url) {
let response = await p1;
let html = await p2;
return url;
}
getUrl('http://www.baidu.com').then((val) => {
console.log(val)
}) //只有等到 p1 和 p2 都执行完毕后才会调用then里面的回调函数输出值,输出:http://www.baidu.com
2.4、async函数的链式用法
async函数返回的值是一个 promise 对象,这意味着我们在执行完一个 async 函数完后,可以在后面接着写 then 等操作。
2.4.1、async 函数返回原始类型值的情况
async 函数如果最后是返回原始类型的值,那么该原始类型值会被当做被 resolve 的值,我们可以在该函数后面加 then 来继续执行操作,获取返回的值。
async function main () {
//返回原始类型值会被包装为一个立即resolve的Promise对象
return 123; //相当于 return Promise.resolve(123),其实也是一个promise对象
}
//此时的链式写法
const data = main() //此时data是一个promise对象,可以加then来继续执行操作
data.then(num => {
console.log(num); //输出123
})
2.4.2、async 函数返回promise对象的情况
其实 async 函数返回原始类型值也相当于是返回一个 promise 对象,此时我们可以在另一个 async 函数里用 await 接收 resolve 出来的值,也可以直接加 then 继续操作。
async function main () {
return new Promise((resolve, reject) => {
resolve(123)
})
} //此时的链式写法
//可以在另一个 async 函数里面接收它的结果
async function main2 () {
const data = await main(); //await后面是一个promise对象,会等到该promise对象将值resolve出来
console.log(data); //输出12,此时的data已经获取到了resolve出来的值。
}
main2()
请注意:async 函数里,在 await 后面返回的值并不是 async 函数返回的值。
//下面的async函数相当于没有返回值,其他函数就接收不到
async function main () {
new Promise((resolve, reject) => {
return 123;
})
} async function main2 () {
const data = await main();
console.log(data); //输出 undefined,因为main函数根本没有返回值。
}
main2()
2.5、async 函数的错误处理
如果await
后面的异步操作出错,或者是await 后面的 promise 对象被 reject,那么async函数后面的语句将不会执行,async 函数会立即返回一个 rejected 状态的promise对象
。
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
}
f().then(v => console.log(v)).catch(e => console.log(e))
// Error:出错了 async function f() {
await Promise.reject('出错了!!');
}
f().then(v => console.log(v)).catch(e => console.log(e)) //输出: 出错了!!
防止出错的方法,也是将其放在try...catch
代码块之中。
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
}catch (e) {}
return await ('hello world'); //这里仍然能执行
}
f().then((data) => {console.log(data);}); //输出: hello world
Promise和async/await的更多相关文章
- node.js异步控制流程 回调,事件,promise和async/await
写这个问题是因为最近看到一些初学者用回调用的不亦乐乎,最后代码左调来又调去很不直观. 首先上结论:推荐使用async/await或者co/yield,其次是promise,再次是事件,回调不要使用. ...
- Promise, Generator, async/await的渐进理解
作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了.Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单 ...
- 异步操作之 Promise 和 Async await 用法进阶
ES6 提供的 Promise 方法和 ES7 提供的 Async/Await 语法糖都可以更好解决多层回调问题, 详细用法可参考:https://www.cnblogs.com/cckui/p/99 ...
- Promise及Async/Await
一.为什么有Async/Await? 我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢? 答案其实也显而易见:Promise虽然跳出了异步嵌套的怪 ...
- callback vs async.js vs promise vs async / await
需求: A.依次读取 A|B|C 三个文件,如果有失败,则立即终止. B.同时读取 A|B|C 三个文件,如果有失败,则立即终止. 一.callback 需求A: let read = functio ...
- Callback, Promise和Async/Await的对比
Callback, Promise和Async/Await的对比 Callback Hell getData1(function (data1) { console.log('我得到data1了') ...
- 异步Promise及Async/Await最完整入门攻略
一.为什么有Async/Await? 我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢? 答案其实也显而易见:Promise虽然跳出了异步嵌套的怪圈, ...
- “setTimeout、Promise、Async/Await 的区别”题目解析和扩展
解答这个题目之前,先回顾下JavaScript的事件循环(Event Loop). JavaScript的事件循环 事件循环(Event Loop):同步和异步任务分别进入不同的执行"场所& ...
- promise 进阶 —— async / await 结合 bluebird
一.背景 1.Node.js 异步控制 在之前写的 callback vs async.js vs promise vs async / await 里,我介绍了 ES6 的 promise 和 ES ...
- 异步Promise及Async/Await可能最完整入门攻略
此文只介绍Async/Await与Promise基础知识与实际用到注意的问题,将通过很多代码实例进行说明,两个实例代码是setDelay和setDelaySecond. tips:本文系原创转自我的博 ...
随机推荐
- Object not locked by thread before notify() in onPostExecute
Ask Question Asked 5 years, 4 months ago Active 3 years, 9 months ago Viewed 56k time 41 2 I try to ...
- 爬楼梯问题,yield学习总结
问题起源: 一个人爬楼梯,一步可以迈一级,二级,如果楼梯有N级,要求编写程序,求总共有多少种走法. 简单的一个递归思想,只要爬到了N-1层,或者爬到N-2层,则认定下一步只有一种走法.所以再去找寻N- ...
- java8 stream 用法收集
public class Test1 { public static void main(String[] args) { List<Integer> numbers = new Arra ...
- Java程序控制结构
分支结构 循环结构 跳转结构 一.分支结构 概念:程序从两条或多条路径中选择一条去执行,这种结构称为分支结构 1. if结构 特点: ① 条件表达式的形式可以为: boolean的变量 boolean ...
- BAT程序员常用的开发工具,建议收藏!
今天给大家推荐一批 BAT 公司常用的开发工具,个个好用,建议转发+收藏. 阿里篇 一.Java 线上诊断工具 Arthas Arthas 是阿里巴巴 2018 年 9 月开源的一款 Java 线上诊 ...
- [APIO2019] [LOJ 3145] 桥梁(分块+并查集)(有详细注释)
[APIO2019] [LOJ 3145] 桥梁(分块+并查集)(有详细注释) 题面 略 分析 考试的时候就感觉子任务4是突破口,结果却写了个Kruskal重构树,然后一直想怎么在线用数据结构维护 实 ...
- java NIO介绍
前言 我们在写java程序的时候,为了进行优化,把全部的精力用在了处理效率上,但是对IO的关注却很少.这也可能是由以前java早期时JVM在解释字节码时速度慢,运行速率大大低于本地编译代码,因此以前往 ...
- 13 个设计 REST API 的最佳实践
原文 RESTful API Design: 13 Best Practices to Make Your Users Happy 写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口 ...
- ofbiz16.11.04(环境搭建)
ofbiz16.11.04(环境搭建) 版本说明: ofbiz 16.11.04 下载地址:http://ofbiz.apache.org/download.html gradle 4.9 下载地址: ...
- 使用hash表进行数组去重
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列 ...