--文末附视频教程+源码javascript
new Promise
时,须要传递一个 executor 执行器,执行器马上执行(同步执行),executor 接受两个参数,分别是 resolve(成功) 和 reject(失败)。java
promise 有 3 个状态:git
- pending:初始状态,既不是成功,也不是失败状态。
- fulfilled:成功状态,意味着操做成功完成。
- rejected:失败状态,意味着操做失败。
复制代码
const PENDING = Symbol('PENDING')
const RESOLVED = Symbol('RESOLVED')
const REJECTED = Symbol('REJECTED')
// Promise 构造函数
function Promise (executor) {
// 当前的状态,默认是 pending
this.status = PENDING
// 保存回调函数,由于 then 能够调用屡次,因此以数组保存
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
// 成功值
this.value = undefined
// 拒绝的缘由
this.reason = undefined
// resolve、reject 是用来改变状态,
// 而且根据 then 方法注册回调函数的顺序依次调用回调函数
// resolve 是执行成功后调用的函数
const resolve = (value) => {
// 若是状态不是 pending,说明状态已经改变,不能再发生变化
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// reject 是执行失败后调用的函数
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
// 使用 try...catch... 捕捉代码执行过程当中可能抛出的异常
try {
// 执行器默认会当即执行
executor(resolve, reject)
} catch(e) {
// 若是执行时发生错误(包括手动抛出的异常),等同于执行失败
reject(e)
}
}
复制代码
实现 Promise 的 then
方法,then
方法有两个可选参数,onFulfilled
和 onRejected
,而且必须返回一个 promise 对象。 若是 onFulfilled
或 onRejected
返回的是一个 promise,会自动执行这个 promise,并采用它的状态。若是成功则将成功的结果向外层的下一个 then 传递。es6
Promise.prototype.then = function(onFulfilled, onRejected) {
// onFulfilled 和 onRejected 是可选的,这里须要对不传的时候作兼容处理
// onFulfilled 若是不是函数,就构建一个函数,函数直接返回结果。
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected 若是不是函数,就构建一个函数,函数直接抛出异常。
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason;
}
let promise2 = new Promise((resolve, reject) => {
// 状态为 resolved 或 rejected 时,主要是 new Promise 时执行器里面调用 resolve/reject 是同步的
if (this.status === RESOLVED) {
// 使用 setTimeout (宏任务),确保 onFulfilled 和 onRejected 方法异步执行,也确保 promise2 已经定义,
// 若是不使用 setTimeout,会致使执行 resolvePromise(promise2, x, resolve, reject) 时 promise2 未定义而报错。
setTimeout(() => {
// try...catch... 捕捉代码错误或手动抛出的异常,报错或异常看成执行失败处理。异步代码的报错没法被外层的 try...catch... 捕获
try {
const x = onFulfilled(this.value)
// x 多是 promise 也多是普通值,x 本次 then 调用中 onFulfilled 或 onRejected 回调函数返回的结果,须要传递给下一个 then 的回调函数
// 使用公共方法 resolvePromise 处理不一样状况,并实现 x 值的传递。
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
return
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
// 状态为 pending 时,主要是 new Promise 时执行器里面调用 resolve/reject 是异步的
if (this.status === PENDING) {
// 由于是异步的,不知道什么时候执行完成,因此这里先存好回调函数的调用(订阅),等状态改变后再执行(发布)
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
复制代码
有部分同窗可能会认为 then
是在 promise 状态改变后(即有返回值后)才执行,其实 then
是当即执行,是 onFulfilled
和 onRejected
才在状态改变后执行。github
Promise 解决程序(promise resolution procedure) 是一个抽象的操做,须要输入一个 promise 和一个值,咱们表示为[[Resolve]](promise, x)
。shell
这里咱们定义公用方法 resolvePromise
来实现这个过程。npm
resolvePromise
主要实现的功能是:json
判断 promise2
和 x
是否指向同一对象,若是是 promise2
执行失败而且使用 TypeError 做为执行失败的缘由。数组
例如:promise
const p = new Promise((resolve, reject) => resolve(1))
let promise2 = p.then(() => {
// x
return promise2
})
复制代码
判断 x
是否是一个 promise 对象,若是是就经过调用 resolve/reject 获取状态并向下个 then
传递 。
若是 x
是一个普通对象/值,则直接将 x
做为结果值向下个 then
传递。
下面是代码实现:
const resolvePromise = (promise2, x, resolve, reject) => {
// 若是 promise2 和 x 指向同一对象, promise2 执行失败而且使用 TypeError 做为执行失败的缘由
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<promise>'))
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// 防止屡次调用成功或者失败
let called;
try {
// 首先存储一个指向 x.then 的引用,而后测试并调用该引用,以免屡次访问 x.then 属性
// 预防取 x.then 的时候错误,例如: .then 是经过 Object.defineProperty 定义的,定义的 get() {}(getter) 可能代码错误或抛出异常
let then = x.then
// 没用 x.then 判断由于怕再次取 .then 的时候出错。例如:经过 Object.defineProperty 定义的 then 可能第一次调用不报错,第二次调用报错或屡次调用返回的值可能不一样
if (typeof then === 'function') {
// 若是 then 是一个函数,则认为 x 是一个 promise,以 x 为 它的 this 调用它, then 调用完成就会取到 x 的状态,采用 x 的状态返回
// 而且传递两个回调函数做为参数,第一个参数是 resolvePromise,第二个参数是 rejectPromise
then.call(x, y => {
if (called) {
return
}
called = true
// y 是 x 调用 then 后成功的结果,采用这个结果
// y 可能仍是一个 promise,因此进行递归调用,直到结果是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, r => {
// r 是调用 x.then 后报错或异常,再也不判断是不是 promise,直接传递
if (called) {
return
}
called = true
reject(r); // 失败结果向下传递
});
} else {
// 普通对象,直接传递给下一个 then
resolve(x)
}
} catch (e) {
// 发生代码错误或手动抛出异常,则当执行失败处理并以 e 为失败缘由
if (called) {
return
}
called = true
reject(e)
}
} else {
// 普通值,直接传递给下一个 then
resolve(x)
}
}
复制代码
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
复制代码
deferred
的做用:
使用 promise-aplus-test
工具须要用到这个方法
这个方法能够减小代码嵌套
例如:
const Promise = require('./pormise')
const fs = require('fs')
const readfile = url => {
return new Promise((resolve, reject) => { // 一层嵌套
fs.readFile(url, 'utf-8', (err, data) => { // 二层嵌套
if(err) reject(err)
resolve(data)
})
})
}
readfile('./package.json').then(data => console.log(data))
复制代码
使用 deferred
:
const readfile = url => {
let dfd = Promise.defer()
// 减小了一层嵌套
fs.readFile(url, 'utf-8', (err, data) => {
if(err) dfd.reject(err)
dfd.resolve(data)
})
return dfd.promise
}
复制代码
测试使用工具 promises-aplus-test
安装:npm install -g promises-aplus-test
测试:promise-aplus-test promise.js
使用本文提供的 github源码 则能够直接运行如下命令:
// 安装依赖工具
npm install
// 运行测试指令
npm run test
复制代码
上面已经实现了 Promise 的核心部分代码,但原生的 Promise 还提供一些其余的方法。
有时须要将现有对象转为 Promise 对象,Promise.resolve()
方法就起到这个做用。
Promise.resolve()
等价于下面的写法。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
复制代码
Promise.resolve
方法的参数分红四种状况。
Promise.resolve
不作任何修改,原封不动返回thenable
对象,Promise.resolve
方法会将这个对象转为 Promise 对象,而后就当即执行thenable
对象的then
方法。then
方法的对象,或根本就不是对象,Promise.resolve
方法返回一个新的 Promise 对象,状态为resolved
resolved
状态的 Promise 对象。Promise.resolve = function (param) {
if (param instanceof Promise) {
return param;
}
return new Promise((resolve, reject) => {
if (param && param.then && typeof param.then === 'function') {
setTimeout(() => {
param.then(resolve, reject);
});
} else {
resolve(param);
}
});
}
复制代码
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
Promise.reject()
方法的参数,会原封不动地做为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
复制代码
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
复制代码
上面代码中,Promise.all()
方法接受一个数组做为参数,p1
、p2
、p3
都是 Promise 实例,若是不是,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()
方法的参数能够不是数组,但必须具备 Iterator 接口,且返回的每一个成员都是 Promise 实例。
p
的状态由p1
、p2
、p3
决定,分红两种状况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
// 存放结果,.all 传入的参数是数组,返回结果也是数据
let result = []
// 使用计数器,记录多个异步并发问题
let index = 0
if (promises.length === 0) {
resolve(result)
} else {
// 处理返回值
function processValue(i, data) {
result[i] = data
// 计数器记录的个数等于传入的数组长度,说明所有认为已完成,能够返回结果
if (++index === promises.length) {
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i]
// 判断当前的处理对象是 promise 仍是普通值
if (isPromise(current)) {
// 取当前的处理对象的执行结果,若是有一个执行失败,则直接 reject
current.then(data => {
processValue(i, data)
}, reject)
} else {
processValue(i, current)
}
}
}
})
}
复制代码
Promise.race()
方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
复制代码
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.race()
方法的参数与Promise.all()
方法同样,若是不是 Promise 实例,就会先调用下面讲到的Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return;
} else {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((data) => {
resolve(data);
return;
}, (err) => {
reject(err);
return;
});
}
}
});
}
复制代码
Promise.prototype.catch
方法是 .then(null, rejection)
或 .then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}
复制代码
finally
方法用于指定无论 Promise 对象最后状态如何,都会执行的操做。该方法是 ES2018 引入标准的。
Promise.prototype.finally = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
}, (err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
});
}
复制代码