Promise用法 & Promise原理实现

概述

Promise最早由社区提出并实现,典型的一些库有Q,when, bluebird等;它们的出现是为了更好地解决JavaScript中异步编程的问题,传统的异步编程最大的特点就是地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Promise则可以让我们通过链式调用的方法去解决回调嵌套的问题,使我们的代码更容易理解和维护,而且Promise还增加了许多有用的特性,让我们处理异步编程得心应手

Promise创建

Promise本意是承诺,它有三个状态(等待态、成功态、失败态), 默认是等待态。new Promise 时传递一个执行器 (executor),且执行器立即执行

//Promise是一个类
console.log(2)
new Promise(function ((resolve, reject) => {
    console.log(1)
))
console.log(3);
输出结果如下:
2
1
3
复制代码

Promise.then

每个promise实例都一个then方法,then方法有两个参数(成功和失败),成功会有成功的值,失败会有失败的原因

let promise = new Promise((resolve, reject) => {
    //resolve('hello');
    reject('hello')
});
promise.then(data => {
    console.log('data', data);
}, err=>{
    console.log('err', err)
})
输出结果如下:
//data hello
err hello
复制代码

且同一个promise可以then多次

let promise = new Promise((resolve, reject) => {
    //resolve('hello');
    reject('hello')
});
promise.then(data => {
    console.log('data', data);
}, err=>{
    console.log('err', err)
});

promise.then(data => {
    console.log('data', data);
}, err=>{
    console.log('err', err)
})
输出结果如下:
//data hello
//data hello
err hello
err hello
复制代码

基本的promise实现

Promise/A+ 规范的内容比较多,详情查看promisesaplus.com/,实现 Promise 逻辑时会根据实现的部分介绍相关的Promise/A+规范内容。

class Promise {
    constructor(executor) {
        //默认的状态
        this.status = 'pending';
        //成功的值 
        this.value = undefined;
        //失败的原因
        this.reason = undefined;
        //成功存放的数组
        this.onResolvedCallbacks = [];
        //失败存放的数组 
        this.onRejectedCallbacks = [];
        //默认让执行器执行
        let resolve = (value) => {
            if (this.status === 'pending') {
                this.status = 'fulfilled'; //成功
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn()); 
            }
        }

        let reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected'; //失败
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try{
            executor(resolve, reject);
        }catch(e) {
            reject(e);
        }
    }
    then(onFufilled, onRejected) {
        if (this.status === 'fulfilled') {
            onFufilled(this.value);
        }
        if (this.status === 'rejected') {
            onRejected(this.reason);
        }
          if (this.status === 'pending') {
            this.onResolvedCallbacks.push(() => {
                onFufilled(this.value);
            });
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason);
            })
        }
    }
}

module.exports = Promise;
复制代码

验证实现的promise

example 1
let Promise = require('./Promise.js')
let promise = new Promise((resolve, reject) => {
    resolve('hello')
});
promise.then(data => {
    console.log('data', data);
}, err=>{
    console.log('err', err)
});
输出结果如下
data hello


example 2
let Promise = require('./Promise.js')
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
       resolve('hello')
    })
});
promise.then(data => {
    console.log('data', data);
}, err=>{
    console.log('err', err)
});
输出结果如下
data hello
复制代码

Promise链式调用

  • Promise内部抛出异常,直接走失败执行函数
let fs = require('fs');
function read (path, enconding) {
    return new Promise((resolve, reject) => {
        throw new Error('内部抛出异常');
        fs.readFile(path, enconding, (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}
read('./3.txt', 'utf8').then(data => {
    console.log(data);
}, err => {
    console.log("error", err)
})
输出结果如下:
error Error: 内部抛出异常
复制代码
  • Promise解决多个异步回调(有关联)
//成功的回调,或者失败的回调执行后返回一个promise
//会将这个promise的结果传给下一个then中
//如果返回普通值,会将这个普通值传递到下一次then的成功参数
read('./1.txt', 'utf8').then(data => {
    console.log('then1', data)
    return read(data, 'utf8');
}, err => {
    console.log("err1", err)
}).then(data => {
    console.log('then2', data);
}, err => {
    console.log("err2", err)
});
注: 1.txt 文本内容为 "2.txt"
    2.txt 文本内容为 "数据"
输出结果如下:
then1 2.txt
then2 数据 

read('./1.txt', 'utf8').then(data => {
    console.log('then1', data)
    return read(data, 'utf8');
}, err => {
    console.log("err1", err)
}).then(data => {
    console.log('then2', data);
    return data
}, err => {
    console.log("err2", err)
}).then(data => {
    console.log('then3', data)
}).then(data => {
    console.log('then4', data)
});
输出结果如下:
then1 2.txt
then2 数据 
then3 数据 
then4 undefined

read('./1.txt', 'utf8').then(data => {
    console.log('then1', data)
    return read(data, 'utf8');
}, err => {
    console.log("err1", err)
}).then(data => {
    console.log('then2', data);
    return data
}, err => {
    console.log("err2", err)
}).then(data => {
    console.log('then3', data)
}).then(data => {
    console.log('then4', data);
    throw new Error('xxxxxxxxxxxxxxxx');
}).then(null, err => {
    console.log("err5", err)
}).then(data => {
    console.log('then5 成功', data)
}, err=> {
    console.log("err6 失败")
})
输出结果如下
then1 2.txt
then2 数据 
then3 数据 
then4 undefined
err5 Error: xxxxxxxxxxxxxxxx
then5 成功 undefined

read('./1.txt', 'utf8').then(data => {
    console.log('then1', data)
    return read(data, 'utf8');
}, err => {
    console.log("err1", err)
}).then(data => {
    console.log('then2', data);
    return data
}, err => {
    console.log("err2", err)
}).then(data => {
    console.log('then3', data)
}).then(data => {
    console.log('then4', data);
    throw new Error('xxxxxxxxxxxxxxxx');
}).then().then(data => {
    console.log('then5 成功', data)
}).catch(err => {
    console.log("catch", err)
})
输出结果如下
then1 2.txt
then2 数据 
then3 数据 
then4 undefined
catch Error: xxxxxxxxxxxxxxxx
复制代码
  • Promise解决多个异步回调(无关联)
Promise.all 多个promise都成功后,执行成功回调,如果有一个失败,走失败回调
let fs = require('fs');
function read(path, enconding) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, enconding, (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}
let pRead1 = read('./1.txt', 'utf-8');
let pRead2 = read('./2.txt', 'utf-8');
Promise.all([pRead1, pRead2]).then(res => {
    console.log(res);
})
输出结果如下
[ '2.txt', '数据 ' ]
复制代码
Promise.race,多个promise执行,谁先执行完,就用先执行的结果
let fs = require('fs');
function read(path, enconding) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, enconding, (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}
let pRead1 = read('./1.txt', 'utf-8');
let pRead2 = read('./2.txt', 'utf-8');

Promise.race([pRead2, pRead1]).then(res => {
    console.log(res);
}, err => {
    console.log('err', err)
})
输出结果如下
2.txt
复制代码
  • Promise.resolve、Promise.reject
Promise.resolve(123).then(data => {
    console.log(data)
});

Promise.reject('err 123').then(null, err => {
    console.log(err)
})
复制代码

实现Promise链式调用(promise实现链式调用返回一个新的promise)

class Promise {
    constructor(executor) {...}
    then(onFufilled, onRejected) {
        onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err;};
        let promise2;
        promise2 = new Promise((resolve, reject) => {
            if (this.status === 'fulfilled') {
                setTimeout(() => {
                    try{
                        let x = onFufilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e) {
                        reject(e)
                    }
                }, 0);
             
            }
            if (this.status === 'rejected') {
                setTimeout(() => {
                    try{
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e) {
                        reject(e)
                    }
                }, 0);
            }
            if (this.status === 'pending') {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onFufilled(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;
        //将每个 Promise 实例调用 then 后返回的新 Promise 实例称为 promise2,将 then 回调返回的值称为 x;
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    /*
    如果 promise2 和 x 为同一个对象,由于 x 要将执行成功或失败的结果传递 promise2 的 then 方法回调的参数,
    因为是同一个 Promise 实例,此时既不能成功也不能失败(自己不能等待自己完成),造成循环引用,
    这种情况下规定应该抛出一个类型错误来回绝;
    */
    if (promise2 === x) {
        return reject(new TypeError('循环引用'))
    }
    let called;
    //如果 x 是一个对象或者函数且不是 null
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        /*
         如果 x 是对象,防止 x 是通过 Object.defineProperty 添加 then 属性,并添加 get 和 set 监听,
         如果在监听中抛出异常,需要被捕获到
        */
        try{
            let then = x.then //如果是对象,试着取下then 
            
            if (typeof then === 'function') {
                //x.then 是一个函数,就当作 x 是一个 Promise 实例,
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    //resolve的结果依旧是promise,继续解析
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(y); 
                })
            }else {
                //如果 x.then 不是函数,则说明 x 为普通值,直接调用 promise2 的 resolve 方法将 x 传入,
                resolve(x);
            }
        }catch(e) { //取then出错,不在继续执行
            if (called) return;
            called = true;
            reject(e);
        }
    }else {
        //不满足条件说明该返回值就是一个普通值,直接执行 promise2 的 resolve 并将 x 作为参数传入;
        resolve(x);
    }
}

module.exports = Promise;
复制代码

验证promise链式调用

let Promise = require('./Promise')
let promise = new Promise((resolve, reject) => {
    resolve('hello');
})
promise.then(data => {
    console.log('then1', data);
    return 4
}).then(data => {
    console.log("data", data)
}).then(() => {
    throw new Error("error");
}).then(() => {
    console.log("success");
}, err => {
    console.log(err);
}).then(() => {
    console.log("成功");
}, () => {
    console.log("失败");
});
输出结果如下
then1 hello
data 4
Error: error
成功
复制代码

验证参数是否穿透

let p1 = new Promise((resolve, reject) => resolve("ok"));

let p2 = p1.then().then(data => {
    console.log(data);
    throw new Error("出错了");
});

p2.then().then(null, err => console.log(err));
输出结果如下
ok
Error: 出错了
复制代码

实现catch方法

class Promise {
    constructor(executor) {...}
    then(onFufilled, onRejected) {...}
    catch(onRejected) {
        return this.then(null, onRejected)
    }
}
复制代码

实现Promise.resolve方法

Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
        resolve(value);
    })
}

复制代码

实现Promise.reject方法

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}
复制代码

实现Promise.all方法

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let result = [];
        let idx = 0;
        let processData = (index, data) => {
            idx++;
            result[index] = data;
            if (promises.length === idx) {
                resolove(result)
            }
        }

        for (let i = 0; i < promises.length; i++) {
            promises[i].then(data => {
                processData(i, data);
            }, reject)
        }
    })
}
复制代码

实现Promise.race方法

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve, reject)
        }
    })
}
复制代码

测试Promise的实现代码(测试包 promises-aplus-tests)

  • 全局安装 npm install promises-aplus-tests -g
promises-aplus-tests 文件
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
//https://www.pandashen.com/2018/06/13/20180613193626/#more  <-- 更多描述请查看
复制代码

转载于:https://juejin.im/post/5b418e12e51d45194d4fc3be

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值