[JS]源码学习Promise
Posted ouyangshima
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JS]源码学习Promise相关的知识,希望对你有一定的参考价值。
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、resolved/fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。Promise也有一些缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise
// 构造方法接收一个回调
constructor(executor)
this._status = PENDING // Promise状态
this._value = undefined // 储存then回调return的值
this._resolveQueue = [] // 成功队列, resolve时触发
this._rejectQueue = [] // 失败队列, reject时触发
// 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向,
//否则找不到this._resolveQueue
let _resolve = (val) =>
//把resolve执行回调的操作封装成一个函数,放进setTimeout里,
//以兼容executor是同步代码的情况
const run = () =>
if (this._status !== PENDING) return // 状态只能由pending到fulfilled或rejected
this._status = FULFILLED // 变更状态
this._value = val // 储存当前value
// 这里之所以使用一个队列来储存回调,是为了实现"then方法可以被同一个promise调用多次"
// 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
while (this._resolveQueue.length)
const callback = this._resolveQueue.shift()
callback(val)
setTimeout(run)
// 实现同resolve
let _reject = (val) =>
const run = () =>
if (this._status !== PENDING) return // 状态只能由pending到fulfilled或rejected
this._status = REJECTED // 变更状态
this._value = val // 储存当前value
while (this._rejectQueue.length)
const callback = this._rejectQueue.shift()
callback(val)
setTimeout(run)
// new Promise()时立即执行executor,并传入resolve和reject
executor(_resolve, _reject)
// then方法,接收一个成功的回调和一个失败的回调
then(resolveFn, rejectFn)
//如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
typeof resolveFn !== 'function' ? resolveFn = value => value : null
typeof rejectFn !== 'function' ? rejectFn = reason =>
throw new Error(reason instanceof Error ? reason.message : reason);
: null
// return一个新的promise
return new MyPromise((resolve, reject) =>
// 把resolveFn重新包装一下,再push进resolve执行队列,
//这是为了能够获取回调的返回值进行分类讨论
const fulfilledFn = value =>
try
// 执行第一个(当前的)Promise的成功回调,并获取返回值
let x = resolveFn(value)
// 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
catch (error)
reject(error)
// reject同理
const rejectedFn = error =>
try
let x = rejectFn(error)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
catch (error)
reject(error)
switch (this._status)
// 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
case PENDING:
this._resolveQueue.push(fulfilledFn)
this._rejectQueue.push(rejectedFn)
break;
// 当状态已经变为resolve/reject时,直接执行then回调
case FULFILLED:
fulfilledFn(this._value) // this._value是上一个then回调return的值
break;
case REJECTED:
rejectedFn(this._value)
break;
)
Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
catch、finally对象方法
//catch方法其实就是执行一下then的第二个回调
//catch()方法返回一个Promise,并且处理拒绝的情况。
//它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
catch(rejectFn)
return this.then(undefined, rejectFn)
//finally方法
//finally()方法返回一个Promise。
//在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。
//在finally之后,还可以继续then。并且会将值原封不动的传递给后面的then
finally(callback)
return this.then(
//执行回调,并returnvalue传递给后面的then
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => throw reason ) //reject同理
)
resolve、reject、all、race静态方法
//静态的resolve方法
//Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。
//如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
//返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
//否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
static resolve(value)
//根据规范, 如果参数是Promise实例, 直接return这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
//静态的reject方法
//Promise.reject()方法返回一个带有拒绝原因的Promise对象。
static reject(reason)
return new MyPromise((resolve, reject) => reject(reason))
//静态的all方法
//Promise.all(iterable)方法返回一个 Promise 实例,
//此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
//或参数中不包含 promise 时回调完成(resolve);
//如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),
//失败原因的是第一个失败 promise 的结果。
static all(promiseArr)
let index = 0
let result = []
return new MyPromise((resolve, reject) =>
promiseArr.forEach((p, i) =>
//Promise.resolve(p)用于处理传入值不为Promise的情况
MyPromise.resolve(p).then(
val =>
index++
result[i] = val
if (index === promiseArr.length)
resolve(result)
,
err =>
reject(err)
)
)
)
//静态的race方法
//Promise.race(iterable)方法返回一个 promise,
//一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
static race(promiseArr)
return new MyPromise((resolve, reject) =>
//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
for (let p of promiseArr)
//Promise.resolve(p)用于处理传入值不为Promise的情况
MyPromise.resolve(p).then(
value =>
resolve(value) //注意这个resolve是上边new MyPromise的
,
err =>
reject(err)
)
)
以上是关于[JS]源码学习Promise的主要内容,如果未能解决你的问题,请参考以下文章