Promise基本用法
Promise凭什么解决了“回调地狱”
主要有三个原因:
then链式调用,实现回调函数延迟绑定
通过then的链式调用,我们可以在定义了要同步执行的代码后再通过then执行其定义的回调函数。
比如:
返回值穿透
通过promise中then的调用,可以将其返回值作为一个新的promise,在之后的then中调用其上一个then的返回值,比如我们常用的fetch:
1 2 3
| fetch('http://example.com/api').then(res => res.json()).then(jsonRes => { console.log(jsonRes) })
|
在上面的逻辑中,我们在第一个then里返回response响应的json格式的数据,以供下一个then调用,第二个then中的jsonRes已经是第一个res.json()的结果了。
错误冒泡处理
在以往的回调函数中,我们往往需要同时定义success和error两种情况,而对于多个调用,这样会让开发变得非常的麻烦,要编写很多的错误处理,而在promise中,所有的错误都会冒泡到catch之中,我们可以在只编写一个回调事件用于处理出错的catch事件。
Promise的then为什么是一个微任务
在JavaScript回调函数的调用有三种方式:
- 同步调用,在回调函数拿到结果前不执行任何其他代码。
- 异步调用,作为宏任务插入到宏任务队列的队尾,在宏任务队列中按次序执行。
- 异步调用,作为微任务插入到当前宏任务的微任务队列中,即在执行当前宏任务的最后执行微任务队列。
显然,如果把promise的then作为第一种方式,将会导致cpu资源的浪费,出现同步的阻塞问题,这是很浪费计算机宝贵资源的(实际上就是同步异步本身的性能差异),与此同时还会有一个问题,那就是将其定义成同步任务,无法实现延迟绑定回调函数的效果
延迟绑定:也就是在定义好要执行的同步函数之后,再从then中传入回调函数。
而异步调用,若是作为宏任务,如果宏任务队列很长,要执行的代码很多,就会出现严重的延迟,而对于大部分的场景,我们都是希望能尽早完成回调函数的调用的。
所以Promise的then自然而然地成为了一个微任务,既使用了异步回调保证效率,也保证了回调函数调用的实时性。
Promise手写
关于手写一个promise,难点和要点都在于then的链式调用
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| class MyPromise { PENDING = 'pending' FULFILLED = 'fulfilled' REJECTED = 'rejected' result = undefined; value = undefined; onResolveArr = []; onRejectArr = []; status = this.PENDING constructor(executor) { if (this.status === this.PENDING) { try { executor(this.resolve, this.reject); } catch (error) { this.reject(error) } } } resolve = (value) => { if (this.status === this.PENDING) { this.status = this.FULFILLED; this.value = value; this.onResolveArr.forEach(resolveFn => (resolveFn(this.value))) } } reject = (result) => { if (this.status === this.PENDING) { this.status = this.REJECTED; this.result = result; this.onRejectArr.forEach(rejectFn => (rejectFn(this.result))) } } then(resolveCallBack, rejectCallBack) { typeof resolveCallBack === 'function' ? null : resolveCallBack = value => value; typeof rejectCallBack === 'function' ? null : rejectCallBack = result => { throw TypeError(result instanceof Error ? result.message : result) } return new MyPromise((resolveFn, rejectFn) => { this.onResolveArr.push(() => { try { let res = resolveCallBack(this.value) res instanceof MyPromise ? res.then(resolveFn, rejectFn) : resolveFn(res) } catch (error) { rejectFn(error) } }) this.onRejectArr.push(() => { try { let res = rejectCallBack(this.result) res instanceof MyPromise ? res.then(resolveFn, rejectFn) : resolveFn(res) } catch (error) { rejectFn(error) } }) }) } catch (reject) { this.then(null, reject) } }
new MyPromise((resolve, reject) => { setTimeout(() => { Math.random() * 100 > 50 ? resolve("ok") : reject("no") }, 1000); }).then(res => { console.log(res) return "next" }).then(res => { console.log(res) }).catch(res => { console.log(res) })
|