一、Promise
的使用
创建一个promise
实例,方便处理异步操作
var p = new Promise(function(resolve,reject){
setTimeout(function(){
resolve("success")
},1000);
console.log("创建一个新的promise");
})
p.then(function(x){
console.log(x)
})
// 输出:创建一个新的promise
// 输出:success
promise.then()
链式调用
var p = new Promise(function(resolve, reject) {
resolve()
});
p.then(...).then(...).then(...)
其他方法:
Promise.resolve()
Promise.all()
Promise.race()
二、Promise原理剖析
1.Promise 凭借什么消灭了回调地狱
2.为什么Promise要引入微任务
3.Promise 如何实现链式调用
正文。。。
三、手写Promise/A+
规范的Promise
Promise/A+
规范:
『术语』:
promise
是一个对象或者函数,该对象或者函数有一个then
方法thenable
是一个对象或者函数,用来定义then
方法value
是promise
状态成功时的值reason
是promise
状态失败时的值
要求:
promise
必须有3个状态,pending
、fulfilled(resolved)
和rejected
- 当处于
pending
状态的时候,可以转移到fulfilled(resolved)
或rejected
状态 - 当处于
fulfilled(resolved)
状态或rejected
状态的时候,则不可变
- 当处于
- 一个
promise
必须有一个then
方法then
方法接受两个参数:promise.then(onFulfilled, onRejected)
onFulfilled
方法表示状态从pending——>fulfilled(resolved)
时所执行的方法onRejected
表示状态从pending——>rejected
所执行的方法。
- 为了实现链式调用,then方法必须返回一个promise。
promise2=promise1.then(onFulfilled,onRejected)
1.Promise
基础结构
- 执行回调数组:当
resolve()
延迟执行时,Promise
的状态为pending
,不应立即执行成功调用的函数,需要把它存起来,直到执行resolve
再执行成功调用的函数 - 链式调用:
Promise
里的链接调用是返回一个新的Promise
对象 - 异步执行:用
setTimeout
模拟微任务(JS事件循环执行顺序上和原生Promise
有区别),把回调放入,等待确保异步执行
基础Promise
:
const isFunction = variable => typeof variable === 'function'
// 定义三个常量表示状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled',
const REJECTED = 'rejected'
class MyPromise {
constructor(func) {
this.onFulfilledArray = [] // 成功回调数组
this.onRejectedArray = [] // 失败回调数组
this.value = void 0
this.reason = void 0
this.status = PENDING
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(val) {
if (this.status !== PENDING) return
const self = this
const doResolve = () => {
if (self.status === PENDING) {
self.status = FULFILLED
self.value = val
// resolve里面将所有成功的回调拿出来执行
while (self.onFulfilledArray.length) {
const fn = self.onFulfilledArray.shift()
fn()
}
}
}
setTimeout(doResolve(), 0)
}
reject(err) {
if (this.status !== PENDING) return
const self = this
const doReject = () => {
if (self.status === PENDING) {
self.status = REJECTED
self.reason = err
// resolve里面将所有失败的回调拿出来执行
self.onRejectedArray.forEach(fn => {
fn()
})
}
}
setTimeout(doReject, 0)
}
then(onFulfilled, onRejected) {
let nextPromise = void 0
switch (this.status) {
case PENDING:
nextPromise = new MyPromise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let res = onFulfilled(self.value)
resolve(res)
} catch (error) {
reject(error)
}
})
this.onRejectedArray.push(() => {
try {
let res = onRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
})
})
break
case FULFILLED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = onFulfilled(self.value)
resolve(res)
} catch (e) {
reject(e)
}
})
break
case REJECTED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = onRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
})
onRejected(self.reason)
break
}
return nextPromise
}
catch(err) {
// 默认没有成功,只有失败
return this.then(void 0, err)
}
}
2.链式调用进阶版
以上的
Promise
存在一个问题,如果链式调用中Promise
返回的是普通值,应该把值包装成新的Promise
对象
要求:
- 每个
then
方法都返回一个新的Promise
对象(重点) - 如果
then
方法返回了一个Promise
对象,则需要查看它的状态- 如果状态是成功,则调用resolve方法,把成功的状态传递给它;
- 如果是失败的,则把失败的状态传递给下一个Promise对象
- 如果
then
方法中返回的是一个原始数据类型值(如 Number、String 等)就使用此值包装成一个新的Promise
对象返回 - 如果
then
方法中没有return
语句,则返回一个用undefined
包装的Promise
对象 - 如果 then 方法没有传入任何回调,则继续向下传递(值的传递特性)
- 如果是循环引用则需要抛出错误
修改then
方法,增加resolvePromise
函数
...
function resolvePromise(x, nextPromise, resolve, reject) {
// 处理三种情况:
// 1.循环引用
// 2.x 为MyPromise
// 3.x为基础类型
if (x === nextPromise) {
// x 和 nextPromise 指向同一对象,循环引用抛出错误。防止死循环
return reject(new TypeError('循环引用'))
} else if (x && (typeof x === 'object' || isFunction(x))) {
// x 是对象或者函数
let called = false // 避免多次调用
try {
let then = x.then // 判断对象是否有 then 方法
if (isFunction(then)) {
// then 是函数,就断定 x 是一个 MyPromise(根据Promise A+规范)
then.call(
x,
function(y) {
// 调用返回的MyPromise,用它的结果作为下一次then的结果
if (called) return
called = true
resolvePromise(y, nextPromise, resolve, reject) // 递归解析成功后的值,直到它是一个普通值为止
},
function(r) {
if (called) return
called = true
reject(r) // 取then时发生错误了
}
)
} else {
resolve(x) // 此时,x是一个普通对象
}
} catch (e) {
reject(e)
}
} else {
// x 是原始数据类型 / 没有返回值,这里即是undefined
resolve(x)
}
}
class MyPromise {
constructor(func) {
...
}
...
then(onFulfilled, onRejected) {
const self = this
// 如果onFulfilled不是函数,给一个默认函数,返回value
let realOnFulfilled = onFulfilled
if (!isFunction(realOnFulfilled)) realOnFulfilled = value => value
let realOnRejected = onRejected
if (!isFunction(onRejected)) {
realOnRejected = reason => {
if (reason instanceof Error) {
throw reason
} else {
throw new Error(reason)
}
}
}
let nextPromise = void 0
switch (this.status) {
case PENDING:
nextPromise = new MyPromise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let res = realOnFulfilled(self.value)
resolvePromise(res, nextPromise, resolve, reject)
// resolve(res)
} catch (error) {
reject(error)
}
// onFulfilled(self.value)
})
this.onRejectedArray.push(() => {
try {
let res = realOnRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
// onRejected(self.reason)
})
})
break
case FULFILLED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = realOnFulfilled(self.value)
resolvePromise(res, nextPromise, resolve, reject)
} catch (e) {
reject(e)
}
})
// onFulfilled(self.value)
break
case REJECTED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = realOnRejected(self.reason)
resolvePromise(res, nextPromise, resolve, reject)
} catch (e) {
reject(e)
}
})
onRejected(self.reason)
break
}
return nextPromise
}
...
}
3.Promise.prototype.finally
实现
有两大重点:
- 无论当前这个 Promise 对象最终的状态是成功还是失败 ,finally 方法里的回调函数都会执行一次
- 在 finally 方法后面可以继续链式调用 then 方法,拿到当前这个 Promise 对象最终返回的结果
挂载在MyPromise.property
原型上,或者类似catch
实现:
MyPromise.prototype.finally = function(callback) {
return this.then(
data => {
return MyPromise.resolve(callback()).then(() => data)
},
err => {
return MyPromise.resolve(
callback().then(() => {
throw err
})
)
}
)
}
4.Promise.all()
和Promise.race()
实现
Promise.all()
:可将多个Promise
实例包装成一个新的Promise
实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject
失败状态的值
...
function isPromise(val) {
return val && typeof val.then === 'function'
}
class MyPromise() {
...
}
...
MyPromise.all = function(promises) {
if (!Array.isArray(promises)) {
throw new Error('Not a array')
}
return new MyPromise((resolve, reject) => {
let result = []
let times = 0 // 计数器
function processData(index, val) {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i]
if (isPromise(p)) {
// MyPromise对象
p.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, p) // 普通值
}
}
})
}
Promise.race()
在执行多个异步操作中,不管结果是成功状态还是失败状态,只保留取第一个执行完成的异步操作的结果。其他的方法仍会执行,但结果会被抛弃
...
class MyPromise() {
...
}
...
MyPromise.race = function(promises) {
if (!Array.isArray(promises)) {
throw new Error('Not a array')
}
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
p.then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
}
}
})
}
5.最终手写版源码
代码太长,直接参考源码仓库 examples | lianpf.github。欢迎star
参考
思路很清晰的两篇文章:
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!