Promise
概念
Promise可以用于顺序的执行一些异步的代码。比如说,需要先获取用户的信息,得到了用户的基本信息才可以去用基本信息中的userId拿到用户的权限信息。 Promise可以处理一个不知道返回结果的方法。比如,获取学校的信息需要调用接口,但是我们没法保证这个接口每次调用100%正确(有可能会因为网络问题等出现异常)
状态state、结果result
这俩均为Promise对象的内部属性,可以访问 一个Promise执行过程中,这两个内部属性的变换

resolve(): pending -> fulfilled
reject():pending -> rejected
如果一个Promise执行完了resolve或者reject,那么这个Promise的状态是settled

使用
new Promise(function(resolve, reject) { });
|
接收一个resolve函数和reject函数。Promise接收的函数的函数体会自动的执行,执行的结果如果成功就会执行resolve失败就会执行reject,也可以自己调用resolve和reject
resolve、reject执行结果的接收
使用then()来接收
promise.then( function(result) { }, function(error) { } );
|
then也可以接收两个参数,第一个是处理resolve执行的结果(该函数将在 promise resolved 且接收到结果后执行),第二个是reject执行的结果(该函数将在 promise rejected 且接收到 error 信息后执行) 当然也可以只写一个函数作为参数
.then((result) => {}) .then(null, errorHandlingFunction)
|
上面代码块仅处理error也可以使用catch
let promise = new Promise((resolve, reject) => { setTimeout(() => reject(new Error("Whoops!")) , 1000); });
|
catch可以捕获Promise以及resolve产生的错误。且在链式调用下可以捕获前面的的所有错误
最后可以用finally来执行收尾工作
new Promise((resolve, reject) => { })
.finally(() => stop loading indicator)
|
finally会接收上面执行成功的value或者执行失败的error
Promise链
一个Promise的resolve和reject可以返回,返回的值为一个新的Promise
loadScript("/article/promise-chaining/one.js") .then(script => loadScript("/article/promise-chaining/two.js")) .then(script => loadScript("/article/promise-chaining/three.js")) .then(script => { one(); two(); three(); });
|
如果.then()这样调用没有传入onFulfilled就会透传,直到遇到有回调的then方法
.catch()就和then不同,如果前面的Promise Reject了,就会去找有没有调用catch方法,会跳过之间的所有then()(注意是没有添加 onRejected 回调的.then())
自己实现一个MyPromise
Promise是异步的,关键在于如何实现”异步”的功能。不阻塞主线程的执行,这里自己实现使用queueMicrotask来执行resolve和reject
thenable方法也是难点,要考虑传入的两个方法的返回值的情况,Promise A+也就这些东西
function MyPromise(callback) { this.state = MyPromiseState.Pending; this.onFulfilleds = []; this.onRejecteds = []; this.value = null
this.resolve = data => { if (data instanceof MyPromise) { return data.then(this.resolve, this.reject) } queueMicrotask(() => { if (this.state === MyPromiseState.Pending) { this.state = MyPromiseState.Fulfilled; this.value = data for (let i = 0; i < this.onFulfilleds.length; i++) { this.onFulfilleds[i](data); } } }) }
this.reject = err => { queueMicrotask(() => { if (this.state === MyPromiseState.Pending) { this.state = MyPromiseState.Rejected; this.value = err; for (let i = 0; i < this.onRejecteds.length; i++) { this.onRejecteds[i](err); } } }) }
try { callback(this.resolve, this.reject) } catch (error) { this.reject(error) } }
MyPromise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
let self = this let newPromise = new MyPromise((resolve, reject) => { if (self.state === MyPromiseState.Pending) { self.onFulfilleds.push(() => { try { let x = onFulfilled(self.value) solutionX(newPromise, x, resolve, reject) } catch (error) { reject(error) } }) self.onRejecteds.push(() => { try { let x = onRejected(self.value) solutionX(newPromise, x, resolve, reject) } catch (error) { reject(error) } }) }
if (self.state === MyPromiseState.Fulfilled) { queueMicrotask(() => { try { let x = onFulfilled(self.value) solutionX(newPromise, x, resolve, reject) } catch (error) { reject(error) } }); }
if (self.state === MyPromiseState.Rejected) { queueMicrotask(() => { try { let x = onRejected(self.value) solutionX(newPromise, x, resolve, reject) } catch (error) { reject(error) } }); } }); return newPromise; }
const solutionX = (promise, x, promiseResolve, promiseReject) => { if (Object.is(promise, x)) { return promiseReject(new TypeError('x is not same to be promise')) } if (x instanceof MyPromise) { if (x.state === MyPromiseState.Pending) { x.then( value => solutionX(promise, value, promiseResolve, promiseReject), promiseReject ) } if (x.state === MyPromiseState.Fulfilled || x.state === MyPromiseState.Rejected) { x.then( promiseResolve, promiseReject ) } return } let called = false; if (!x) { promiseResolve(x) } else if (typeof x === 'function' || typeof x === 'object') { try { let then = x.then if (typeof then === 'function') { then.apply( x, [ y => { if (called) return called = true solutionX(promise, y, promiseResolve, promiseReject) }, r => { if (called) return called = true promiseReject(r) } ] ) } else { promiseResolve(x) } } catch (error) { if (called) return called = true
promiseReject(error) } } else { promiseResolve(x) } }
|
Promise A+规范测试
pnpx promises-aplus-tests myPromise.js
|
结果通过所有872个测试用例
回调地狱问题
比如上面代码块中需要按顺序以此请求对应的js文件,如果不使用Promise
loadScript('1.js', function(error, script) { if (error) { handleError(error); } else { loadScript('2.js', function(error, script) { if (error) { handleError(error); } else { loadScript('3.js', function(error, script) { if (error) { handleError(error); } else { } }); } }); }});
|
没有发生错误的情况下,又会执行loadScript,之后在其中又判断是否有错误,没错误继续load…,上面的代码就是“回调地狱”
Promise定义的一些静态方法
Promise.all
概念:并行执行多个Promise,并且等待所有的Promise执行完毕
Promise.all([ new Promise(resolve => setTimeout(() => resolve(1), 3000)), new Promise(resolve => setTimeout(() => resolve(2), 2000)), new Promise(resolve => setTimeout(() => resolve(3), 1000)) ]) .then(alert);
|
all()接收一个数组,里面是Promise对象,返回一个res数组,并且会按照Promise数组的顺序组装res结果数组 如果执行的任意一个Promise被reject,则整个Promise.all()会立即的reject并且返回error
Promise.race
和上面的.allAPI都可以同时执行多个Promise,但是.race仅等待第一个状态为settled的Promise(settled就是Promise要么resolve要么reject)
Promise.any
和.any也是可以同时执行多个,但.any等待第一个fulfilled状态的Promise,并返回这个Promise(fulfilled就是Promise对象resolve了,而不是reject)
Promise.allSettled
和.all()都是接收一个Promise对象数组,但是不会像all()碰到reject状态的Promise就返回,而是等待数组的所有Promise执行完毕(如果是resolve,返回的{status: 'fulfilled', value: ''}对象,如果是reject,返回的{status: 'rejected', reason: ''}对象)