学习Async和Promise
-
promise
-
主要用于异步计算
-
使异步操作队列化,按照期望得顺序执行,返回符合预期得结果
-
可以在对象之间传递和操作promise,帮助我们处理队列
-
两个特点
- 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
-
easy-example
new Promise(resolve => { setTimeout(() => { resolve('hello'); }, 2000); }).then(res => { console.log(res); },)
-
-
.then()
- 接收两个函数作为参数,分别代表 fulfilled (成功)和rejected(失败)
- .then()返回一个新的Promise实例,所以它可以链式调用
- 当前的Promise状态改变时,.then() 根据其最终状态,选择特
定的状态响应函数执行 - 如果返回新的Promise,那么下一级 .then 会在新的promise状
态改变之后执行 - 状态响应函数可以返回新的promise, 或其他值,不返回值也
可以,我们可以认为它返回了一个null - 如果返回其他任何值,则会立即执行下一级 .then()
-
错误处理
- 第一种: reject(‘错误信息’) .then(() => {}, () => {错误处理逻辑})
new Promise((resolve, reject) => { setTimeout(() => { reject('bye'); }, 2000) }) .then((val) => { console.log(val) }, (err) => { console.log('Error:' + err); })
- 第二种
new Promise((resolve) => { setTimeout(() => { throw new Error('bye'); }, 2000) }).then((value) => { console.log(value); }).catch(error => { console.log('Error', error); })
- 多个then嵌套
new Promise(resolve => { console.log('step 1'); setTimeout(() => { resolve('100'); }, 1000) }).then(value => { // value = 100 return new Promise(resolve => { console.log('step-1-1'); setTimeout(() => { resolve('110'); }, 1000) }) }).then(value => { //value = 110 console.log('step-1-2'); return value; }).then(value => { // value = 110 console.log('step-1-3'); return value; // value = 110 }).then(value => { console.log(value); // value = 110 console.log('step 2'); })
- 运行结果
- catch() + then()错误处理
- 第一种
console.log('here we go...') new Promise(resolve => { setTimeout(() => { resolve(); }, 2000) }) .then(() => { console.log('start'); throw new Error('test error'); }) .catch(err => { console.log('I catch:', err); }) .then(() => { console.log('arrive here'); }) .then(() => { console.log('...and here'); }) .catch(err => { console.log('No, I catch:', err) })
- 运行结果
- 第二种
console.log('heoe we go..') new Promise(resolve => { setTimeout(() => { resolve(); }, 2000) }) .then(() => { console.log('start'); throw new Error('test error'); }) .catch(err => { console.log('1 catch: ', err); throw new Error('another error'); }) .then(() => { console.log('arrlve here'); }) .then(() => { console.log('...and here'); }) .catch(err => { console.log('No, I catch', err) })
- 运行结果
-
结论: 抛出错误变成rejected状态,catch也会返回一个promise实例,并且时resolved状态。
-
Promise.all () 批量执行
-
Promise.all([p1,p2,p3])用于将多个promise实例,包装成一个新的Promise实例,返回得实例就是普通得Promise它接收一个数组作为参数,可以不是数组,但必须具有 Iterator接口,数组里面可以是Promise对象,也可以是其他值,所有子Promise都完成,该Promise完成,返回值是全部值得数组,有任何一个失败,该Promise失败,返回值是第一个失败得子Promise结果
function cutUp() { console.log('开始切菜'); var p = new Promise(function (resolve, reject) { // 做一些异步操作 setTimeout(function () { console.log('切菜完毕'); }, 1000) }) return p; } // 烧水 function boil() { console.log('开始烧水'); var p = new Promise(function (resolve, reject) { // 做一些异步操作 setTimeout(function () { console.log('烧水完毕'); }, 1000) }) return p; } Promise.all([cutUp(), boil()]) .then((result) => { console.log(result); })
- 运行结果
- 注:all里面的promise实例会同时运行而不是按照顺序一个个运行
-
-
Promise.race ()
- 与promise类似,区别在于它有任意一个完成就算完成,常用与异步操作和定时器放在一起,如果定时器先触发,就认为超时,告知用户。
-
promise.finally( ()=>{ } )
- 用于指定不管 Promise 对象最后状态如何,都会执行的操作。
-
Promise.allSettled()
- Promise.allSettled()方法接受一组 Promise 实例作为参
数,包装成一个新的 Promise 实例。只有等到所有这些
参数实例都返回结果,不管是fulfilled还是rejected,包装
实例才会结束。
- Promise.allSettled()方法接受一组 Promise 实例作为参
-
Promise.any()
- Promise.any()方法接受一组 Promise 实例作为参
数,包装成一个新的 Promise 实例。只要参数实例
有一个变成fulfilled状态,包装实例就会变成
fulfilled状态;如果所有参数实例都变成rejected状
态,包装实例就会变成rejected状态//还只是一个提案用不了
- Promise.any()方法接受一组 Promise 实例作为参
-
Promise.resolve()
将现有对象转为 Promise 对象
- 1.参数是一个 Promise 实例:直接返回一个promise实例不改变
- 2.参数是一个thenable对象
let thenable = { then: function(resolve, reject) { resolve(42); } };
- 3.参数不是具有then方法的对象,或根本就不是对象
let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
- 4.不带有任何参数
const p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });
-
Promise.reject(reason)
- Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。也就和resolve一样的。只不过这个是错误处理。
- 用于处理不知道函数是异步还是同步的时候,让函数按照原来的顺序执行 。
const f = () => console.log('now'); Promise.resolve().then(f); console.log('next'); //但是还是提案 用不了,用了会说没有定义
const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); //现在可以利用new一个promise时候就会执行的特点来执行try功能 console.log('next');
-
async 函数
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then指令的语法糖。
- 基本语法
- async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
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 函数有多种使用形式。
// 函数表达式 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 () => {};
- async函数返回一个 Promise 对象
- async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() { return 'hello world'; } f().then(v => console.log(v))
- async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f() { await Promise.reject('出错了'); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // 出错了
- async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
- Await命令
- 正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
- await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。
- 任何一个await语句后面的 Promise 对象变为reject状态,那么整
个async函数都会中断执行。async function f() { await Promise.reject('出错了'); await Promise.resolve(console.log(1)); // 不会执行 } f();
- 若不想中断两种方法
async function f() { try { await Promise.reject('出错了'); } catch(e) { } return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) //
async function f() { await Promise.reject('出错了') .catch(e => console.log(e)); return await Promise.resolve('hello world'); } f() .then(v => console.log(v))
- 注意点
- await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中
- 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
- await命令只能用在async函数之中,如果用在普通函数,就会报错
- async 函数可以保留运行堆栈
const a = () => { b().then(() => c()); }; //执行函数b的时候,a已经执行完了,错误栈就只有b或者c const a = async () => { await b(); c(); //执行函数b的时候,a正在执行,所以错误栈就有a,b,c。 };
- 注意点
- 若不想中断两种方法
- 顶层await
- 允许在模块的顶层独立使用await命令。这个提案的目的,是借用await解决模块异步加载的问题 //依然只是一个提案
- 主要用在数据库的读取,import方法,让他们这些顶层同时执行。交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,然后再继续向下执行。
// import() 方法加载 const strings = await import(`/i18n/${navigator.language}`); // 数据库操作 const connection = await dbConnector(); // 依赖回滚 let jQuery; try { jQuery = await import('https://cdn-a.com/jQuery'); } catch { jQuery = await import('https://cdn-b.com/jQuery'); }
- 总结一下:promise就是把原来复杂的回调函数简化了一哈,让函数看起来更加的容易读,方便维护,async和await就是一对,async可以来修饰函数,方法让他们成为一个promise实例。await是来执行那些promise函数的时候修饰用的。async和await就是promise的进阶版。