Promise
基本介绍
Promise 是异步编程的一种解决方案,可以将它看成一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
▼ Promise 三种状态
pending
—— 进行中
fulfilled
—— 已成功
rejected
—— 已失败
▼ Promise 特点
- 对象的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态的改变只有两种可能:
pending -> fulfilled
、pending -> rejected
▼ Promise API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| new Promise((resolve, reject) => { resolve(value) reject(value) })
promise.then(value => { }, error => { })
promise.catch(error => { })
promise.finally(error => { })
Promise.all([p2, p3, p4])
Promise.race([p1, p2, p3])
Promise.allSettled([p1, p2, p3])
Promise.any([p1, p2, p3])
Promise.resolve($.ajax('/whatever.json'))
Promise.reject('error')
|
为什么使用
传统的异步回调,在多层依赖的情况下,会有“回调地狱”的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| setTimeout(() => { console.log(111); setTimeout(() => { console.log(222); setTimeout(() => { console.log(333); setTimeout(() => { console.log(444); ... }, 4000); }, 3000); }, 2000) }, 1000);
|
而如果换成Promise就不会有这样的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function f1() { return new Promise((resolve, reject) => { setTimeout(() => resolve(111), 1000); }).then(data => console.log(data)); } function f2() { return new Promise((resolve, reject) => { setTimeout(() => resolve(222), 2000); }).then(data => console.log(data)); } function f3() { return new Promise((resolve, reject) => { setTimeout(() => resolve(333), 3000); }).then(data => console.log(data)); } function f4() { return new Promise((resolve, reject) => { setTimeout(() => resolve(444), 4000); }).then(data => console.log(data)); } f1().then(f2).then(f3).then(f4);
|
面试问题
▼ 已有一个Promise对象的实例A,封装一个函数让他执行超时时抛出错误
1 2 3 4 5 6
| function timeoutError (t) { return new Promise ((resolve, reject) => { resolve(A) setTimeout(reject, t) }) }
|
使用的知识点为resolve(A)
实际等同于new Promise(resolve => A)
Async/Await
async
与await
是ES2017中提出的,是Promise
的语法糖,使得异步代码书写起来更像同步代码,更易于阅读和理解。
我们可以用上面的例子来对比一下多层回调的情况:
1 2 3 4 5 6 7
| async doIt function () { await f1() await f2() await f3() await f4() }
|
乍看之下可能没什么差别,但是如果有需要参数传递时,就可以看出Async/Awiat
的优越性:
1 2 3 4 5
| function waiter (time) { return new Promise((resolve, reject) => { setTimeout(() => resolve(time), time); }) }
|
1 2 3 4 5 6 7
| waiter(100) .then(time1 => { return waiter(time1).then(time2 => [time1, time2]) }) .then(([time1, time2]) => { doSomething(time1, time2) })
|
1 2 3 4 5
| async doIt function () { await time1 = waiter(100) await time2 = waiter(time1) doSomething(time1, time2) }
|
可以看出,使用Async/Awiat
的代码更加的简洁和好理解,如果有需要处理错误,则也和同步代码一样使用try catch
即可。