我的promise能实现什么?

1:解决回调地狱,实现异步

2:可以链式调用,可以嵌套调用

3:有等待态到成功态的方法,有等待态到失败态的方法

4:可以衍生出周边的方法,如Promise.resolve(),Promise.reject(),Promise.prototype.then(),Promise.prototype.catch(),Promise.all() // 所有的完成

5. 可以根据自己的需求调节自己的promise

let PromiseA = require('./PromiseA');

const promiseA = new PromiseA((resolve, reject) => {
resolve(new PromiseA((resolve,reject)=>{
setTimeout(()=>{
resolve(100)
},1000)
}))
}) promiseA.then(data=>{
console.log(data)
})

下面开始实现promise,首先创造三个常量,等待,成功,失败

const PENDING = 'PENDING';   // 等待状态
const RESOLVED = 'RESOLVED'; // 成功状态
const REJECTED = 'REJECTED'; // 失败状态

然后创造一个promiseA类,里面有constructor,then方法,catch方法。这里的catch其实就是失败的then,即then(null,(err)=>{...})

class PromiseA {
constructor(){...}
then(){...} 
    catch(err){
        return this.then(null,err)
    }
}

我们重点关注constructor和then,先来看constructor。这里promiseA默认的状态是等待态,成功的值value默认为undefined,失败的值reason默认为undefined。这里的onResolvedCallbacks和onRejectedCallbacks是一个发布订阅的数组,我们先不管。然后有resolve方法,reject方法。如果传进来是resolve,则改变value,并且让状态改为resolved。resolve里面可能还要promise,如果是的话,则执行它的then方法。reject方法同理,也是改变状态和值。

还有Promise自带一个executor执行器,就是传进来的参数。会立即执行 。 但有可能出错。所以用try,catch包住。 executor里有俩个参数,就是resolve和reject。也就是promise传进来参数的俩个resolve,reject方法(constructor里的resolve和reject)

    constructor(executor) {
this.status = PENDING; // 默认等待状态
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if(value instanceof PromiseA){
value.then(resolve,reject)
return
}
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e)
}
}

然后我们在看看then方法,then方法传进来俩个参数onFulfilled,onRejected。这是俩个方法。一个成功的回调函数,一个失败的回调函数。我们重点看一下,三个状态的执行,即status的走向。

如果是resolve,即执行成功的回调onFulfilled。如果是reject,即执行失败的回调onRejected。如果是等待,即执行一个发布订阅的模式。发布订阅 ( 其实就是,我先将成功的回调函数或者失败的回调函数各自放入对应的数组,即是上面我们跳过的俩个数组onResolvedCallbacks和onRejectedCallbacks 。然后,当状态改变为resolve或者reject的时候,即遍历onResolvedCallbacks或者onRejectedCallbacks执行对应的回调函数 。至此异步就实现了,这个异步解决就是靠发布订阅模式来解决的。)

三个状态有三种走向,成功,失败,等待。无论是哪种状态,最终都会执行成功的回调函数onFulfilled或者失败的回调函数onRejected。至此promise的基础功能就实现了。

    then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
let promise2 = new PromiseA((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(err);
}
}); })
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(err);
}
});
})
}
})
return promise2;
}

接下来我们继续实现链式调用,链式调用的方法就是创建一个新的promise2对象,然后得到之前的promise的then方法的值。是怎么得到之前的then方法里面的值呢?就是上面的promise2和resolvePromise方法。我们接着往下看。

let promise2 = promiseA.then(e=>{
return e
}
)
promise2.then(data=>{
console.log(data,'123')
})

那么继续实现,还是上面的函数。这里我们重点观察这个promise2和resolvePromise. 我们先说promise2,这里的promise2,其实是一个新的promise.也就是说promise的链式调用靠的就是返回一个新的promise.这里把之前的三种状态包起来,目的就是可以让里面得到的结果,执行PromiseA的resolve方法和reject方法。有人可能会说,那么PromiseA的resolve或者reject要还是promise怎么办?这里我们就要用到resolvePromise方法来判断了。所以我们将成功的回调函数resolve换成resolvePromise方法来执行。这里我们要明白,resolve是执行成功的回调函数。不管状态是成功还是失败,如果执行成功都是走向resolve。所以resolve和reject的状态如果执行成功都是走向resolve。

        let promise2 = new PromiseA((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(err);
}
}); })
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(err);
}
});
})
}
})
return promise2;

然后我们在看resolvePromise方法,走到这里,说明是执行成功的回调函数了。传进去的参数有promise2,x,promise2的resolve,promise2的reject。首先promise2等于一个构造函数PromiseA。 这样传进去参数是会报错的。因为执行到这一步,promise2还没有生成。所以是会报错的。

所以我们加一个setTimeout包住它,这样就可以等promise2生成完在执行。上面代码的setTimeout涉及到了事件循环,也就是宏任务和微任务的部分。js执行机制是先执行主线程,然后执行微任务队列,然后进行渲染,然后在执行宏任务队列。宏任务执行完,如果宏任务里还包着js任务,就继续循环反复。直到所有任务执行完成。这里的setTimeout是宏任务,promise2执行就是主线程,主线程在宏任务之前执行。

 resolvePromise(promise2, x, resolve, reject);

刚刚说到setTimeout,这里贴上一段代码。这里的newPromise是在setTimeout前执行的。

console.log(1);

setTimeout(() => {
console.log("我是定时器,延迟0S执行的");
}, 0); new Promise((resolve, reject) => {
console.log("new Promise是同步任务里面的宏任务");
resolve("我是then里面的参数,promise里面的then方法是宏任务里面的微任务");
}).then(data => {
console.log(data);
}); console.log(2);

好的参数都传进去了,接下来我们看resolvePromise的具体方法。就是一个判断回调函数x是不是promise,如果是就在循环拆开。直到不是为止。如果是普通值的话就可以直接返回了。至此,所以的promise库就实现完了。至于后面的all和其他周边方法就是语法糖了。主要核心部分掌握了,后面的周边方法就不算什么了。

function resolvePromise(promise2, x, resolve, reject) {

    if (promise2 === x) {
return reject(new TypeError('返回的promise和当前promise不能是同一个对象哦,会嵌入死循环'))
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x,y=> {
resolvePromise(promise2, y, resolve, reject)
},r=> {
reject(r)
}
)
} else {
resolve(x)
}
} catch (err) {
reject(err)
}
} else {
resolve(x);
}
}

至于constructor里的resolve方法里判断,我们来看看。其实也是一个递归。判断PromiseA里的resolve里面是不是promise,一直拆开。跟上面的方法类似。

        let resolve = (value) => {
if(value instanceof PromiseA){
value.then(resolve,reject)
return
}
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}

而至于这俩行代码,就是解决一个不断向jquery那样then的情况。如果是函数的话,将自己的参数作为结果返回。传递给下一个then。

        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
promiseA.then().then().then().then().then()

最后,如果有什么觉得奇怪的地方,欢迎互相讨论,学习。谢谢!

手写Promise原理的更多相关文章

  1. 手写Promise A+ 规范

    基于ES6语法手写promise A+ 规范,源码实现 class Promise { constructor(excutorCallBack) { this.status = 'pending'; ...

  2. 手写promise

    写在前面: 在目前的前端分开中,我们对于异步方法的使用越来越频繁,那么如果处理异步方法的返回结果,如果优雅的进行异步处理对于一个合格的前端开发者而言就显得尤为重要,其中在面试中被问道最多的就是对Pro ...

  3. 手写Promise看着一篇就足够了

    目录 概要 博客思路 API的特性与手写源码 构造函数 then catch Promise.resolved Promise.rejected Promise.all Promise.race 概要 ...

  4. 手写Promise中then方法返回的结果或者规律

    1. Promise中then()方法返回来的结果或者规律 我们知道 promise 的 then 方法返回来的结果值[result]是由: 它指定的回调函数的结果决定的 2.比如说下面这一段代码 l ...

  5. 前端面试题之手写promise

    前端面试题之Promise问题 前言 在我们日常开发中会遇到很多异步的情况,比如涉及到 网络请求(ajax,axios等),定时器这些,对于这些异步操作我们如果需要拿到他们操作后的结果,就需要使用到回 ...

  6. 手写Promise简易版

    话不多说,直接上代码 通过ES5的模块化封装,向外暴露一个属性 (function(window){ const PENDING = 'pending'; const RESOLVED = 'fulf ...

  7. [转]史上最最最详细的手写Promise教程

    我们工作中免不了运用promise用来解决异步回调问题.平时用的很多库或者插件都运用了promise 例如axios.fetch等等.但是你知道promise是咋写出来的呢? 别怕-这里有本promi ...

  8. 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用

    Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...

  9. js手写'Promise'

    /* * pending:初始化成功 * fulfilled:成功 * rejected:失败 * */ function Promise(executor) {// 执行器 this.status ...

随机推荐

  1. Ubuntu 12.04 搭建TFTP服务器

    吐槽先:在Ubuntu上搭建TFTP服务器,网上搜到一堆资料,可惜基本都是部分能用,至于哪些部分能用还要自己摸索着试出来,郁闷之情仅次于找不到任何资料…… ---------------------- ...

  2. 4)mysqlDB

    (1)首先是变量声明 (2)函数注释 (3)__contruct函数的编写(可有可无) (4)getInstance函数编写(这个是获得这个mysqlDB类的实例对象) 编写代码: (5)然后是其他功 ...

  3. 为Nginx启用目录浏览功能

    今天工作需要,要给客户提供一个patch的下载地址,于是想用nginx的目录浏览功能来做,需要让客户看到指定一个目录下的文件列表,然后让他自己来选择该下载那个文件: 我们都知道在apache下可以配置 ...

  4. java 内存溢出-与gc

    感谢原作者 在日常中我们经常遇到这样的错误:java.lang.OutOfMemoryError: Java heap space. 但是除了heap space 的OutOfMemoryError, ...

  5. PP图|QQ图|正态性检验|K-S检验|S-W检验|

    应用统计学: 物理条件一致时,有理由认为方差是一致的.配对检验可排除物理影响,使方差变小,但是自由度降低了,即样本数变小.二项分布均值假设检验的模型要依据前面的假设条件: PP图统计图要看中间的贴近情 ...

  6. PEAKS|NovoHMM|Nover|DeepNovo|MAYUPercolator|UniprotKB|Swiss-prot|Mascot|SEQUEST|X!Tandem|pFind|MaxQuant|Msconvert|PEPMASS|LC|

    质谱仪: 质谱分析法是先将大分子电离为带电粒子,按质核比分离,由质谱仪识别电信号得到质谱图. Top-down直接得到结果是蛋白. Bottom down使用shutgun方法得到结果是肽段. 由蛋白 ...

  7. C\C++ 位域操作

    几篇较全面的位域相关的文章: http://www.uplook.cn/blog/9/93362/ C/C++位域(Bit-fields)之我见 C中的位域与大小端问题 内存对齐全攻略–涉及位域的内存 ...

  8. MicrosoftOfficeProfessionalPlus2013傻瓜式激活工具

    用微软的office系列,总是提示需要输入秘钥,直接找个破解软件破解算了. 破解软件地址:http://www.3322.cc/soft/10037.html 1.下载解压: 2.点击office系列 ...

  9. 使用apache mail发送邮件错误解决办法

    今天在写发送邮件的程序时发现了以下两个些错误,贴出来跟大家分享分享 希望对大家有帮助. 错误一: Exception in thread "main" java.lang.NoCl ...

  10. Django+Celery框架自动化定时任务开发

    本章介绍使用DjCelery即Django+Celery框架开发定时任务功能,在Autotestplat平台上实现单一接口自动化测试脚本.业务场景接口自动化测试脚本.App自动化测试脚本.Web自动化 ...