all方法 手写promise_史上最最最详细的手写Promise教程

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

别怕~这里有本promisesA+规范,便宜点10元卖给你了。

1、Promise 的声明

首先呢,promise肯定是一个类,我们就用class来声明。

由于new Promise((resolve, reject)=>{}),所以传入一个参数(函数),秘籍里叫他executor,传入就执行。

executor里面有两个参数,一个叫resolve(成功),一个叫reject(失败)。

由于resolve和reject可执行,所以都是函数,我们用let声明。

class Promise{

// 构造器

constructor(executor){

// 成功

let resolve = () => { };

// 失败

let reject = () => { };

// 立即执行

executor(resolve, reject);

}

}

解决基本状态

秘籍对Promise有规定:

Promise存在三个状态(state)pending、fulfilled、rejected

pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)

成功时,不可转为其他状态,且必须有一个不可改变的值(value)

失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)

new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。

new Promise((resolve, reject)=>{reject(reason)}) reject为失败,接收参数reason,状态改变为rejected,不可再次改变。

若是executor函数报错 直接执行reject();

于是乎,我们获得以下代码

class Promise{

constructor(executor){

// 初始化state为等待态

this.state = 'pending';

// 成功的值

this.value = undefined;

// 失败的原因

this.reason = undefined;

let resolve = value => {

// state改变,resolve调用就会失败

if (this.state === 'pending') {

// resolve调用后,state转化为成功态

this.state = 'fulfilled';

// 储存成功的值

this.value = value;

}

};

let reject = reason => {

// state改变,reject调用就会失败

if (this.state === 'pending') {

// reject调用后,state转化为失败态

this.state = 'rejected';

// 储存失败的原因

this.reason = reason;

}

};

// 如果executor执行报错,直接执行reject

try{

executor(resolve, reject);

} catch (err) {

reject(err);

}

}

}

then方法

秘籍规定:Promise有一个叫做then的方法,里面有两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因

当状态state为fulfilled,则执行onFulfilled,传入this.value。当状态state为rejected,则执行onRejected,传入this.value

onFulfilled,onRejected如果他们是函数,则必须分别在fulfilled,rejected后被调用,value或reason依次作为他们的第一个参数

class Promise{

constructor(executor){...}

// then 方法 有两个参数onFulfilled onRejected

then(onFulfilled,onRejected) {

// 状态为fulfilled,执行onFulfilled,传入成功的值

if (this.state === 'fulfilled') {

onFulfilled(this.value);

};

// 状态为rejected,执行onRejected,传入失败的原因

if (this.state === 'rejected') {

onRejected(this.reason);

};

}

}

这下武学初成,可以对付对付江湖小杂毛了,但是对于带setTimeout的江洋大盗还是没辙。

解决异步实现

现在基本可以实现简单的同步代码,但是当resolve在setTomeout内执行,then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们

类似于发布订阅,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以存在同一个数组内。

// 多个then的情况

let p = new Promise();

p.then();

p.then();

成功或者失败时,forEach调用它们

class Promise{

constructor(executor){

this.state = 'pending';

this.value = undefined;

this.reason = undefined;

// 成功存放的数组

this.onResolvedCallbacks = [];

// 失败存放法数组

this.onRejectedCallbacks = [];

let resolve = value => {

if (this.state === 'pending') {

this.state = 'fulfilled';

this.value = value;

// 一旦resolve执行,调用成功数组的函数

this.onResolvedCallbacks.forEach(fn=>fn());

}

};

let reject = reason => {

if (this.state === 'pending') {

this.state = 'rejected';

this.reason = reason;

// 一旦reject执行,调用失败数组的函数

this.onRejectedCallbacks.forEach(fn=>fn());

}

};

try{

executor(resolve, reject);

} catch (err) {

reject(err);

}

}

then(onFulfilled,onRejected) {

if (this.state === 'fulfilled') {

onFulfilled(this.value);

};

if (this.state === 'rejected') {

onRejected(this.reason);

};

// 当状态state为pending时

if (this.state === 'pending') {

// onFulfilled传入到成功数组

this.onResolvedCallbacks.push(()=>{

onFulfilled(this.value);

})

// onRejected传入到失败数组

this.onRejectedCallbacks.push(()=>{

onRejected(this.value);

})

}

}

}

解决链式调用

我门常常用到new Promise().then().then(),这就是链式调用,用来解决回调地狱

1、为了达成链式,我们默认在第一个then里返回一个promise。秘籍规定了一种方法,就是在then里面返回一个新的promise,称为promise2:promise2 = new Promise((resolve, reject)=>{})

将这个promise2返回的值传递到下一个then中

如果返回一个普通的值,则将普通的值传递给下一个then中

2、当我们在第一个then中return了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值

秘籍则规定onFulfilled()或onRejected()的值,即第一个then返回的值,叫做x,判断x的函数叫做resolvePromise

首先,要看x是不是promise。

如果是promise,则取它的结果,作为新的promise2成功的结果

如果是普通值,直接作为promise2成功的结果

所以要比较x和promise2

resolvePromise的参数有promise2(默认返回的promise)、x(我们自己return的对象)、resolve、reject

resolve和reject是promise2的

class Promise{

constructor(executor){

this.state = 'pending';

this.value = undefined;

this.reason = undefined;

this.onResolvedCallbacks = [];

this.onRejectedCallbacks = [];

let resolve = value => {

if (this.state === 'pending') {

this.state = 'fulfilled';

this.value = value;

this.onResolvedCallbacks.forEach(fn=>fn());

}

};

let reject = reason => {

if (this.state === 'pending') {

this.state = 'rejected';

this.reason = reason;

this.onRejectedCallbacks.forEach(fn=>fn());

}

};

try{

executor(resolve, reject);

} catch (err) {

reject(err);

}

}

then(onFulfilled,onRejected) {

// 声明返回的promise2

let promise2 = new Promise((resolve, reject)=>{

if (this.state === 'fulfilled') {

let x = onFulfilled(this.value);

// resolvePromise函数,处理自己return的promise和默认的promise2的关系

resolvePromise(promise2, x, resolve, reject);

};

if (this.state === 'rejected') {

let x = onRejected(this.reason);

resolvePromise(promise2, x, resolve, reject);

};

if (this.state === 'pending') {

this.onResolvedCallbacks.push(()=>{

let x = onFulfilled(this.value);

resolvePromise(promise2, x, resolve, reject);

})

this.onRejectedCallbacks.push(()=>{

let x = onRejected(this.value);

resolvePromise(promise2, x, resolve, reject);

})

}

});

// 返回promise,完成链式

return promise2;

}

}

完成resolvePromise函数

秘籍规定了一段代码,让不同的promise代码互相套用,叫做resolvePromise

如果 x === promise2,则是会造成循环引用,自己等待自己完成,则报“循环引用”错误

let p = new Promise(resolve => {

resolve(0);

});

var p2 = p.then(data => {

// 循环引用,自己等待自己完成,一辈子完不成

return p2;

})

1、判断x

Otherwise, if x is an object or function,Let then be x.then

x 不能是null

x 是普通值 直接resolve(x)

x 是对象或者函数(包括promise),let then = x.then 2、当x是对象或者函数(默认promise)

声明了then

如果取then报错,则走reject()

如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调

如果成功的回调还是pormise,就递归继续解析 3、成功和失败只能调用一个 所以设定一个called来防止多次调用

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

// 循环引用报错

if(x === promise2){

// reject报错

return reject(new TypeError('Chaining cycle detected for promise'));

}

// 防止多次调用

let called;

// x不是null 且x是对象或者函数

if (x != null && (typeof x === 'object' || typeof x === 'function')) {

try {

// A+规定,声明then = x的then方法

let then = x.then;

// 如果then是函数,就默认是promise了

if (typeof then === 'function') {

// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调

then.call(x, y => {

// 成功和失败只能调用一个

if (called) return;

called = true;

// resolve的结果依旧是promise 那就继续解析

resolvePromise(promise2, y, resolve, reject);

}, err => {

// 成功和失败只能调用一个

if (called) return;

called = true;

reject(err);// 失败了就失败了

})

} else {

resolve(x); // 直接成功即可

}

} catch (e) {

// 也属于失败

if (called) return;

called = true;

// 取then出错了那就不要在继续执行了

reject(e);

}

} else {

resolve(x);

}

}

解决其他问题

1、秘籍规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略

onFulfilled返回一个普通的值,成功时直接等于 value => value

onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误reason => throw err 2、秘籍规定onFulfilled或onRejected不能同步被调用,必须异步调用。我们就用setTimeout解决异步问题

如果onFulfilled或onRejected报错,则直接返回reject()

class Promise{

constructor(executor){

this.state = 'pending';

this.value = undefined;

this.reason = undefined;

this.onResolvedCallbacks = [];

this.onRejectedCallbacks = [];

let resolve = value => {

if (this.state === 'pending') {

this.state = 'fulfilled';

this.value = value;

this.onResolvedCallbacks.forEach(fn=>fn());

}

};

let reject = reason => {

if (this.state === 'pending') {

this.state = 'rejected';

this.reason = reason;

this.onRejectedCallbacks.forEach(fn=>fn());

}

};

try{

executor(resolve, reject);

} catch (err) {

reject(err);

}

}

then(onFulfilled,onRejected) {

// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;

// onRejected如果不是函数,就忽略onRejected,直接扔出错误

onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

let promise2 = new Promise((resolve, reject) => {

if (this.state === 'fulfilled') {

// 异步

setTimeout(() => {

try {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

};

if (this.state === 'rejected') {

// 异步

setTimeout(() => {

// 如果报错

try {

let x = onRejected(this.reason);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

};

if (this.state === 'pending') {

this.onResolvedCallbacks.push(() => {

// 异步

setTimeout(() => {

try {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

});

this.onRejectedCallbacks.push(() => {

// 异步

setTimeout(() => {

try {

let x = onRejected(this.reason);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0)

});

};

});

// 返回promise,完成链式

return promise2;

}

}

大功告成

顺便附赠catch和resolve、reject、race、all方法

class Promise{

constructor(executor){

this.state = 'pending';

this.value = undefined;

this.reason = undefined;

this.onResolvedCallbacks = [];

this.onRejectedCallbacks = [];

let resolve = value => {

if (this.state === 'pending') {

this.state = 'fulfilled';

this.value = value;

this.onResolvedCallbacks.forEach(fn=>fn());

}

};

let reject = reason => {

if (this.state === 'pending') {

this.state = 'rejected';

this.reason = reason;

this.onRejectedCallbacks.forEach(fn=>fn());

}

};

try{

executor(resolve, reject);

} catch (err) {

reject(err);

}

}

then(onFulfilled,onRejected) {

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;

onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

let promise2 = new Promise((resolve, reject) => {

if (this.state === 'fulfilled') {

setTimeout(() => {

try {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

};

if (this.state === 'rejected') {

setTimeout(() => {

try {

let x = onRejected(this.reason);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

};

if (this.state === 'pending') {

this.onResolvedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0);

});

this.onRejectedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onRejected(this.reason);

resolvePromise(promise2, x, resolve, reject);

} catch (e) {

reject(e);

}

}, 0)

});

};

});

return promise2;

}

catch(fn){

return this.then(null,fn);

}

}

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

if(x === promise2){

return reject(new TypeError('Chaining cycle detected for promise'));

}

let called;

if (x != null && (typeof x === 'object' || typeof x === 'function')) {

try {

let then = x.then;

if (typeof then === 'function') {

then.call(x, y => {

if(called)return;

called = true;

resolvePromise(promise2, y, resolve, reject);

}, err => {

if(called)return;

called = true;

reject(err);

})

} else {

resolve(x);

}

} catch (e) {

if(called)return;

called = true;

reject(e);

}

} else {

resolve(x);

}

}

//resolve方法

Promise.resolve = function(val){

return new Promise((resolve,reject)=>{

resolve(val)

});

}

//reject方法

Promise.reject = function(val){

return new Promise((resolve,reject)=>{

reject(val)

});

}

//race方法

Promise.race = function(promises){

return new Promise((resolve,reject)=>{

for(let i=0;i

promises[i].then(resolve,reject)

};

})

}

//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)

Promise.all = function(promises){

let arr = [];

let i = 0;

function processData(index,data){

arr[index] = data;

i++;

if(i == promises.length){

resolve(arr);

};

};

return new Promise((resolve,reject)=>{

for(let i=0;i

promises[i].then(data=>{

processData(i,data);

},reject);

};

});

}

如何验证我们的promise是否正确

1、先在后面加上下述代码

2、npm 有一个promises-aplus-tests插件 npm i promises-aplus-tests -g 可以全局安装 mac用户最前面加上sudo

3、命令行 promises-aplus-tests [js文件名] 即可验证

// 目前是通过他测试 他会测试一个对象

// 语法糖

Promise.defer = Promise.deferred = function () {

let dfd = {}

dfd.promise = new Promise((resolve,reject)=>{

dfd.resolve = resolve;

dfd.reject = reject;

});

return dfd;

}

module.exports = Promise;

//npm install promises-aplus-tests 用来测试自己的promise 符不符合promisesA+规范

作者:卡姆爱卡姆

链接:https://juejin.im/post/5b2f02cd5188252b937548ab

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值