核心

构造函数核心

  • 维护状态变量,只能由pending变为resolve或者reject
  • 维护一个存储结果的变量
  • 维护一个回调数组,执行到then,如果我们传入的立即执行函数没有立即执行resolve或者reject,所以promise的状态还是pending,这时要把then里面的回调函数保存起来。待到resolve或者reject执行后则执行回调数组里存到方法。若传入的立即执行函数直接执行了resolve或者reject此时就不用把回调保存起来,直接执行onResolved或onRejected方法。注意是异步执行。而且是做为微任务的。(下面我们用setTimeout模拟)

then核心

  • 执行时,若当前状态为pending,则把回调保存到回调数组,若为resolve或者reject则直接异步执行(微任务)。如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。即resolve(result)或者reject(result)
  • 每次then都返回一个新的Promise。
  • promise会发生值传透,当then中传入的不算函数,则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。而且每一个无效的then所返回的promise的状态都为resolved。

核心实现

将Promise向外暴露

(function (window) {
/*
Promise构造函数
executor:执行器函数
*/
function Promise(executor) { } // 向外暴露Promise
window.Promise = Promise
})()

构造函数实现

  function MyPromise(exector) {
var self = this;
self.status = 'pending'; // 给promise对象指定status属性,初始值为pending
self.data = undefined; // 给promise对象指定一个存储结果的data
self.callbacks = []; // 每个元素的结构 {onResolved(){},onRejected(){}}
function resolve(value) {
if(self.status !== 'pending') {
return
}
self.status = 'resolved'; // 将状态更改为resolved
self.data = value; // 保存value的值
// 异步调用resolve才会在这里执行
// 如果有待执行的callback函数,立即异步执行回调函数onResolved();
if(self.callbacks.length > 0) {
self.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value);
});
}
} function reject() {
// 如果当前状态不是pending,则不执行
if(self.status !== 'pending'){
return
}
// 将状态改为rejected
self.status = 'rejected';
// 保存value的值
self.data = value;
// 如果有待执行的callback函数,立即异步执行回调函数onResolved
if (self.callbacks.length>0){
self.callbacks.forEach(callbackObj=>{
callbackObj.onRejected(value)
})
}
}
// 立即同步执行exector
// 注意️:当在执行executor的时候,如果执行异常的话,这个promise的状态会直接执行reject方法
try{
// 立即同步执行executor
executor(resolve,reject);
} catch (e) {
// 如果执行器抛出异常,promise对象变为rejected状态
reject(e);
}
}

then实现

 MyPromise.prototype.then = function(onResolved, onReject) {
var self = this;
// 处理值穿透
onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason} return new myPromise((resolve,reject) => {
if(self.status === 'pending') {
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved() {
handle(onResolved)
},
onRejected() {
handle(onRejected)
}
})
} else if (self.status === 'resolved') {
setTimeout(()=>{
handle(onResolved)
})
} else { // 当status === 'rejected'
setTimeout(()=>{
handle(onRejected)
})
}
// 处理函数
function handle(callback) {
try {
const result = callback(self.data)
if (result instanceof MyPromise){
// 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
} catch (e) {
// 如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
}
});
}

其他方法实现

catch

借用then方法

MyPromise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}

finally

借用then方法

  MyPromise.prototype.finally = (onFinally) => {
return this.then((res)=>{
MyPromise.resolve(onFinally()).then(()=> res)
},(reson)=>{
MyPromise.resolve(onFinally()).then(()=> reson)
})
}

resolve

Promise参数可以为如下三种

  • 不是promise
  • 成功状态的promise
  • 失败状态的promise
MyPromise.resolve = function(value){
return new MyPromise((resolve,reject)=>{
if (value instanceof Promise){
// 如果value 是promise
value.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else{
// 如果value不是promise
resolve(value)
}
}
}

reject

MyPromise.reject = function(reason) {
return new MyPromise((resolve,reject)=>{
reject(reason)
})
}

all

  • 入参一般是个由Promise实例组成的数组,但是也可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。若参数如果不是 Promise 实例,就会先调用Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。
  • 返回值是个promise,因为可以使用.then
  • 如果全部成功,状态变为resolved, 并且返回值组成一个数组传给回调
  • 但凡有一个失败,状态变为rejected, 并将error返回给回调
MyPromise.all = (promisesArr) => {
// 返回Promise
return new MyPromise((resolve, reject) => {
let dataArr = new Array(promisesArr.length);
let count = 0;
for (let i = 0; i < promisesArr.length; i++) {
// 在 .then 中收集数据,并添加 .catch,在某一个 Promise 遇到错误随时 reject。
// 这样,在最外面调用 Promise.all().catch() 时也可以 catch 错误信息
    // 判断当前这个元素是否为Promise对象,不是则转为Promise对象
   let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
currentPromise.then(res => {
    dataArr[index] = data;
count++;
// 如果数据收集完了,就把收集的数据 resolve 出去
if (count === promisesArr.length) resolve(dataArr);
  }).catch(err => {
    //如果某一个失败,promise.all()立即执行reject回调。
//但剩余的promise依旧继续执行,只不过对promise.all的结果不会产生影响了
    reject(err)
  });
}
})

注意️:dataArr添加时用下标而不用数组时为了防止顺序错乱

race

返回一个promise对象,状态由第一个完成的promise决定

MyPromise.race = function(promisesArr){
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
for (let i = 0; i < promisesArr.length; i++) {
// 判断当前这个元素是否为Promise对象,不是则转为Promise对象
let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
currentPromise.then(
value => {
// 只要有一个成功,返回的promise的状态九尾resolved
resolve(value)
},
reason => { //只要有一个失败,return的promise状态就为reject
reject(reason)
}
)
})
})
}

allSettled

Promise.allSettled() 方法返回一个在所有给定的 promise 已被决议或被拒绝后决议的 promise,并带有一个对象数组,每个对象表示对应的promise 结果。

Promise.newAllSettled = function (promisesArr) {
return new Promise((resolve, reject) => {
let results = [];
let count = 0;
let promisesArrLength = promisesArr.length;
// 运行所有的 Promise
for (let i = 0; i < promisesArr.length; i++) {
// 判断当前这个元素是否为Promise对象,不是则转为Promise对象
let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
currentPromise.then(res => {
// 当有 Promise 被 resolve 之后,记录 resolve 值和状态,已决 Promise 计数加一
results.push({value: res, status: 'fulfilled'});
count++;
// 全部 Promise 已决,resolve
if (count === promisesArrLength) {
resolve(results);
}
}).catch(err => {
// 当有 Promise 被 reject 后,记录 reject 值和状态,并且已决的 Promise 计数加一
results.push({value: err, status: 'rejected'});
count++;
if (count === promisesArrLength) {
resolve(results);
}
});
}
})
};

参考

https://juejin.im/post/6844904088963022856

https://juejin.im/post/6850037281206566919

https://blog.csdn.net/MichelleZhai/article/details/104475521

https://zhuanlan.zhihu.com/p/60287801

https://zhuanlan.zhihu.com/p/41502945

https://zhuanlan.zhihu.com/p/61681036

https://juejin.im/post/6856213486633304078

https://zhuanlan.zhihu.com/p/107712626

Promise核心实现的更多相关文章

  1. Promise核心原理解析

    作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...

  2. promise核心6 自定义promise

    1.定义整体结构(不写实现) 定义一个自己的promise的库 lib(库的简写) 一个js文件.一个js模块(不能用es6  也不能commjs)(用es5模块语法 ) 匿名函数自调用.IIFE ( ...

  3. promise 核心 几个小问题

    1.如何改变pending的壮体 抛出异常.pending变为rejected  // throw new Error('fail')  内部抛出异常也这样 reason为抛出的error resol ...

  4. promise核心 为什么用promise

    为什么要用promise 1.使用纯回调函数 先指定回调函数,再启动异步任务 答 1.指定回调函数的方式更加灵活 可以在执行任务前,中,后 2.支持链式调用,解决回调地狱问题 什么是回调地狱:回调函数 ...

  5. Promise核心基础

    基础 Promise 抽象表达:是js中进行异步编程的新的解决方案 具体解释:1.从语法上来说是一个构造函数 2.从功能上来说promise对象用来封装一个异步操作并可以获取其结果 状态改变:0.ne ...

  6. javascript基础修炼(7)——Promise,异步,可靠性

    开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 别人是开发者,你也是 Promise技术是[javascript异步编程]这个话题中非常重要的,它一度让我感到熟悉 ...

  7. 分步理解 Promise 的实现

    一个 Promise 的运用: var firstPromise = new Promise(function(resolve,reject){ setTimeout(function(){ var ...

  8. 前端面试?这份手撸Promise请你收下

    前言 现在很多大厂面试前端都会要求能够手动的写出一个Promise,所以这里整理了一份手写的Promise. 绝对详细,功能绝对强大.如果你不了解Promise的基本使用,那么本篇文章可能不太适合你, ...

  9. Generator库co4.6使用及源码分析

    原文链接 http://www.cnblogs.com/ytu2010dt/p/6043947.html co4.x已经抛弃了原来thunk转而结合promise实现. 一:promise proms ...

随机推荐

  1. java 接口一

    一 接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”. 接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口 的子类)来完成.这样将功能的 ...

  2. 2020-05-14:实现分布式Session的过程是怎样的?

    福哥答案2020-05-14: 此答案来自群员:分布式session ,最开始起源是双机热备,当时php1和php2为了共享session,一开始采用samba/nfs,后来php有了插件以后就扔当时 ...

  3. 2020-05-07:具体讲一下CMS流程

    福哥答案2020-05-07: 福哥口诀法:C初并重清(初始标记.并发标记.重新标记.并发清除) 整个过程分为 4 个步骤,包括:初始标记:仅仅只是标记一下 GCRoots 能直接关联到的对象,速度很 ...

  4. canvas图片编辑操作:缩放、移动、保存(PC端+移动端)

    最近在写canvas关于图片的操作,看了网上的代码基本都是不行的,于是就自己写了一个. html代码 <canvas id="myCanvas" width="37 ...

  5. 聊聊Java内省Introspector

    前提 这篇文章主要分析一下Introspector(内省,应该读xing第三声,没有找到很好的翻译,下文暂且这样称呼)的用法.Introspector是一个专门处理JavaBean的工具类,用来获取J ...

  6. Ubuntu无法安装 英伟达显卡

    安装Ubuntu无法正常驱动英伟达,这时需要在启动参数中添加nomodset 如果不会添加参数可以参考这篇文章:安装ubuntu时黑屏三种解决办法

  7. Java 10更新汇总,新的编译器通吃主流编程语言

    早些时候Oracle发布了Java 10,这是Oracle更改发布策略之后的第一版Java,Oracle现已决定每六个月发布一个新的Java版本,周期缩短了,但是并不是说我们要学的更多了,而是说缩短开 ...

  8. PAT 2-09. 装箱问题模拟(20)

    题目链接 :http://www.patest.cn/contests/ds/2-09 解题思路:直接模拟, 记录已经使用的箱子的剩余容量, 如果已经使用的箱子中没有可以放下物品的箱子, 在增加另一个 ...

  9. C++ Templates (1.4 默认模板实参 Default Template Arguments)

    返回完整目录 目录 1.4 默认模板实参 Default Template Arguments 1.4 默认模板实参 Default Template Arguments 可以为模板参数定义默认值,这 ...

  10. ubuntu apt-mirror 同步源到本地

    1.下载 apt-mirror apt-get install apt-mirror 2.修改配置 root@wangjq:/wangjq# cat /etc/apt/mirror.list##### ...