Promise
在平常使用中很是普遍,本身也是零零散散的学习了一部分,直到有一天本身查看axios
的源码时,发现里面大量的应用了Promise
的函数,决定系统的学习一下。本文主要是根据Promise/A+
的规范,进行Promise
的学习和实现;ios
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。 所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise
是一个对象,从它能够获取异步操做的消息。Promise
提供统一的 API,各类异步操做均可以用一样的方法进行处理。git
pengding(进行中,初始状态),fulfilled(已成功)、reject(已失败)
;只有异步操做结果,才可以改变当前的状态,其余的手段没法改变pending->fulfilled和pending变为reject
,只要有这两种状况发生,状态就会凝固,不会在发生改变了pending
的状态时候,没法获得目前是哪个阶段;想要实现某个内容,就应该明确Promise
的方法使用; 使用方法可参照:es6.ruanyifeng.com/#docs/promi…
本次手写也是参照此用法,对输入和输出进行的控制
Promise
做为一个构造函数,其在new的时候就是马上进行执行,根据其属性和行为去构建基础执行流程,new Promise()自己是一个同步的函数,而Promise.then才是异步的函数
;es6
查看Promise A+规范对于状态的要求
Promies/A+规范要求:github
Promise
的状态是pending的时候,可能会转化到 fulfilled或者rejected
状态Promise
状态是filfilled
的时候
Promise
的状态是reject
的时候
const PENDING = 'pending';
const RESOLVED = 'fulfilled'; //成功
const REJECTED = 'rejected' //失败
复制代码
关键点面试
status
resolve
和reject
来接收成功和失败时候状态更改new Promise
时候返回成功和失败状态;//建立Promise的基本类
class Promise {
//看这个属性 可以在原型上使用 看属性是否公用
constructor(executor) {
this.status = PENDING;
//成功的值
this.value = undefined;
//失败的缘由
this.reason = undefined;
//回调函数存储器 主要解决异步处理流程
this.onReslovedCb = []; //成功回调
this.onRejectedCb = []; //失败回调
//成功函数
let resolve = (value) => {
//只有在pending的时候才能够调用
if (this.status == PENDING) {
this.value = value;
this.status = RESOLVED;
this.onReslovedCb.forEach(fn => fn())
}
}
//失败函数
let reject = (reason) => {
//只有在pending的时候才能够调用
if (this.status == PENDING) {
this.reason = reason;
this.status = REJECTED
this.onRejectedCb.forEach(fn => fn())
}
}
try {
//执行器 默认会当即执行
executor && executor(resolve, reject);
} catch (e) {
//执行的时候出现错误
reject(e)
}
}
}
复制代码
关键点then 方法是Promise已经失败/成功时候调用
先看下promise/A+的规范 粗略列举了重要的内容编程
promise.then(onFulfilled, onRejected)
复制代码
onFulfilled,onRejected
是then两个参数
onFulfilled
不是函数,将会被直接忽略 onRejected
不是函数,也会被直接忽略 then
方法可以在同一个promise
上可以被调用屡次
fulfilled/onRejected
,全部的then回调都必须按照他们的调用初始顺序执行其中onFulfilled
、onRejected
是咱们在外部调用的时候传递进行的回调函数;axios
then(onfulfilled, onrejected) {
//1. 参数是可选则的参数 须要进行判断是否存在
onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : data => data
onrejected = typeof onrejected == 'function' ? onrejected : error => {
throw error
}
// 2.若是存在回调函数,则执行内部的回调函数,里面的函数会马上执行
let promise2 = new Promise((resolve, reject) => {
//2.1 Promise 返回成功的时候
if (this.status == RESOLVED) {
// 定时器处理异常 为了保障promise2已经用完了
setTimeout(() => {
//try 执行函数的时候会报错 在then里面的数据
try {
//x 须要判断是不是promise和规整化
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
//2.1-失败的时候
if (this.status == REJECTED) {
setTimeout(() => {
try {
let x = onrejected && onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
// 2,1- pending 若是当前是pending 表示异步的请求还没回来,进行收集内容
if (this.status == PENDING) {
//若是是异步 先订阅好
this.onReslovedCb.push(() => {
//todo...
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.onRejectedCb.push(() => {
//todo...
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return promise2;
}
复制代码
then方法返回一个Promise的函数,注意在进行promise2的建立的时候,咱们在进行处理时候可能获取的到的是underfined的promise2,所以须要开辟宏任务,promise2建立完成的时候在进行调用,而在进行处理的时候,咱们在onfulfilled、和onrejected获得的参数可能不一样,他们收到的参数可能为几种
api
.then(data=>{
return value;
},err=>{
return value
})
复制代码
value
多是一个Promise
,须要执行当前的Promisevalue
字符串 value
为使用者输入,可能存在的值也是不肯定的,所以须要进行判断,而onfulfilled、onRejected
调用后的结果也是不肯定的,所以须要进行类型的判断;then的关键点在于resolvePromise
的函数,这个函数的作用究竟是什么呢?数组
//判断then里面的函数返回值来进行判断 x表示当前onreject
//promise都遵循的规范,所以须要进行兼容写法
function resolvePromise(promise2, x, resolve, reject) {
//判断当前的x是否是promise 是否是同一个 若是是同一个 就不要等待来了
if (promise2 === x) {
return reject(new TypeError("调用存在错误"))
}
//若是x是对象或者函数 判断数据类型
/** * typeof 基本类型 * constructor * instanceof 判断实例 * Object.toString */
if (typeof x === 'object' && typeof x !== null || typeof x == 'function') {
let called; //内部测试的时候,会成功和失败都调用一下
try {
//取返回结果 then有可能经过defineProperty定义的
let then = x.then
//当前存在then方法 姑且是Promise
if (typeof then === 'function') {
//绑定this 到返回的x上,保证不用再次取then的值
then.call(x, y => {
if (called) return;
called = true; //防止屡次调用成功和失败
//y可能仍是promise //采用promise的成功结果向下传递
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return;
called = true;
reject(r) //采用失败结果乡下传递
}) //保证再次取到then的值
} else {
//说明x就是一个普通的对象 直接成功便可
resolve(x)
}
} catch (e) {
//promise 失败 还能进行调用成功
//是一个普通的值 直接让promise2成功便可
if (called) return;
called = true;
reject(e)
}
} else {
return resolve(x)
}
}
复制代码
在then方法执行完后,Promise的实例状态就会改变成resolved、或者reject
,此时then方法须要兼容一异步的调用类型.所以,当进入then函数后,若是当前的promise
的状态仍然是Pending
,则表示当前结果尚未返回,所以须要增长onRejectedCb、onReslovedCb
用来存储当前的执行函数,一旦某一个状态改变,则进行调用该存储列表中的数据,进行回调;promise
then
方法中的状态收集和修改onfulfilled/onrejected
返回的参数类型不管Promise返回成功仍是失败,都会执行该方法
finally()
方法用于指定无论 Promise 对象最后状态如何,都会执行的操做;finally
方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态究竟是fulfilled仍是rejected。这代表finally
方法里面的操做,应该是与状态无关的,不依赖于 Promise 的执行结果。所以能够绑定此事件在当前promise实例的then方法上,在成功的时候回调传入的函数,在失败的时候也进行回调传入的参数;
/** * finally 函数 promise m每次执行后都会进行执行 * @param {*} cb */
Promise.prototype.finally = function (cb) {
//finally 传入函数,不管成功或者失败都会执行
return this.then(data => {
//Promise.resolve 能够等待这个promise完成
return Promise.resolve(cb().then(() => data))
}, err => {
//失败的时候也执行
return Promise.reject(cb().then(() => {
throw err
}))
})
}
复制代码
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
//异常处理 用于指定发生错误时的回调函数。
//promise抛出一个错误,就被catch()方法指定的回调函数捕获
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
复制代码
Promise.all
可用于接收一个数组做为参数,参数能够不是数组,可是必须有Iterator接口,且返回的每一个成员都是Promise的实例,他的结果是根据传入的数据进行变化的
const p = Promise.all([p1, p2, p3]);
复制代码
/** * 所有成功才能成功,一个失败才会失败 * promiseList 表示当前传递的数组对象 */
Promise.all = function (promiseList) {
return new Promise((resolve, reject) => {
let arr = [];
let index = 0;
//解决多个异步并发的问题 计数器
function proceessData(key, value) {
arr[key] = value;
if (++index == promiseList.length) {
resolve(arr)
}
}
for (let i = 0; i < promiseList.length; i++) {
let current = promiseList[i];
if (isPromise(current)) {
current.then((data) => {
proceessData(i, data)
}, (err) => {
console.log("data")
reject(err)
})
} else {
proceessData(i, current)
}
}
})
}
function isPromise(value) {
if ((typeof value === 'object' && value !== null) || typeof value === 'function') {
if (typeof value.then == 'function') {
return true
}
}
return false;
}
复制代码
Promise.race()方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。从字面意义上而言,“管道”返回最早获得的那个
const p = Promise.race([p1, p2, p3]);
复制代码
/** * 方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。 * @param {array} promiseList 传递的参数列表对象 */
Promise.race = function (promiseList) {
// console.log(promiseList)
//将values中的内容包装成promise的
if (!Array.isArray(promiseList)) {
return Promise.resolve();
}
promiseList = promiseList.map(item => {
return !isPromise(item) ? Promise.resolve(item) : item;
});
// 有一个实例率先改变状态则进行操做
return new Promise((resolve, reject) => {
promiseList.forEach((pro, index) => {
pro.then(res => {
resolve(res)
}, err => {
reject(err)
})
})
})
}
复制代码
Promise.allSettled()方法接受一组 Promise 实例做为参数,包装成一个新的 Promise 实例。只有等到全部这些参数实例都返回结果,不论是fulfilled仍是rejected,包装实例才会结束。该方法由 ES2020 引入。
/** * 方法接受一组 Promise 实例做为参数,包装成一个新的 Promise 实例。只有等到全部这些参数实例都返回结果, * 不论是fulfilled仍是rejected,包装实例才会结束 */
Promise.allSettled = function (promiseList) {
return new Promise((resolve, reject) => {
let index = 0;
let arr = [] ;
//用于记录当前的promise的执行状态
function recordRequest(key, value) {
index++;
arr[key] = value;
//选择这种计数的方式,主要是考虑存在异步的流程,等待全部流程都执行完成后在结束
if (index == promiseList.length) {
resolve(arr)
}
}
for (let i = 0; i < promiseList.length; i++) {
current = promiseList[i]
if (isPromise(current)) {
current.then((data) => {
//每执行完成一个,就去增长记录
recordRequest(i, {
status: 'resolve',
value: data
})
}, (err) => {
//失败的promise也记录
recordRequest(i, {
status: 'reject',
reason: err
})
})
} else {
recordRequest(i, {
status: '',
value: current
})
}
}
})
}
复制代码
Promise.any()方法接受一组 Promise 实例做为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;若是全部参数实例都变成rejected状态,包装实例就会变成rejected状态。该方法目前是一个第三阶段的提案 。
/** * Promise.any()方法接受一组 Promise 实例做为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;若是全部参数实例都变成rejected状态,包装实例就会变成rejected状态。该方法目前是一个第三阶段的提案 。 * @param {*} promiseList promise的参数列表 */
Promise.any = function(promiseList){
promiseList = promiseList.map(item => {
return !isPromise(item) ? Promise.resolve(item) : item;
});
let index = 0;
let result=[]
return new Promise((resolve,reject)=>{
for (let i = 0; i < promiseList.length; i++) {
current = promiseList[i]
if (isPromise(current)) {
current.then((data) => {
resolve(data)
}, (err) => {
index++;
result.push(err)
if(index == promiseList.length){
reject(err);
}
})
}
}
})
}
复制代码
有时须要将现有对象转为 Promise
对象,Promise.resolve()
方法就起到这个做用。会返回一个状态为Resolved状态的promise
Promise.resolve(value),其中value的值包含好多种
参数的类型可能存在几种状况
/** * Promis.resolve 函数 * @param {*} values 传递进来的变量函数 */
Promise.resolve = function (values) {
//1.参数是一个 Promise 实例 将原封不动的返回
if (values instanceof Promise) {
return values;
}
return new Promise((resolve, reject) => {
//2.参数是一个含有then对象 具备then方法
//Promise.resolve()方法会将这个对象转为 Promise 对象,而后就当即执行thenable对象的then()方法。
if (isPromise(values)) {
values.then(resolve, reject);
} else {
//3.参数不是具备then()方法的对象,或根本就不是对象 若是参数是一个原始值,或者是一个不具备then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。
//4.参数不是具备then()方法的对象,或根本就不是对象
//5.不带有任何参数
resolve(values)
}
})
}
复制代码
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
/** * //Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。 * 参数为values字符串 */
Promise.reject = function (values) {
return new Promise((resolve, reject) => {
reject(values)
})
}
复制代码
实用场景: 不知道或者不想区分,函数f是同步函数仍是异步操做,可是想用 Promise 来处理它。由于这样就能够无论f是否包含异步操做,都用then方法指定下一步流程,用catch方法处理f抛出的错误。通常就会采用下面的写法。
因为Promise.try
为全部操做提供了统一的处理机制,因此若是想用then方法管理流程,用Promise.try
包装一下,能够更好地管理异常。
Promise.try = function (fn, argumnts = null, ...args) {
if (typeof fn == 'function') {
//马上执行fn函数并进行调用返回
return new Promise(resolve => resolve(fn.apply(argumnts, args)))
} else {
const err = new TypeError(`${typeof fn} ${fn} is not a function`);
return Promise.try(() => {
throw err
});
}
}
复制代码
let p = new Promise()
自己同步执行p.then()
才是异步的执行