来,一起来实现一个符合Promise/A+的Promose(1.0.1版本)

899 阅读7分钟

Promose是什么?(必要的科普)

为解决异步函数的回调陷阱,开发社区不断摸索,终于折腾出 Promise/A+。它的优势非常显著:

  1. 不增加新的语法,可以立刻适配几乎所有浏览器
  2. 以队列的形式组织代码,易读好改
  3. 捕获异常方案也基本可用

这套方案在迭代中逐步完善,最终被吸收进 ES2015。不仅如此,ES2017 中还增加了 Await/Async,可以用顺序的方式书写异步代码,甚至可以正常抛出捕获错误,维护同一个栈。可以说彻底解决了异步回调的问题。

现在大部分浏览器和 Node.js 都已原生支持 Promise,很多类库也开始返回 Promise 对象,更有各种降级适配策略。Node.js 7+ 则实装了 Await/Async。


Promose的基本用法

let Mypromise = require('./Promise') // 这个是自己写的 Promise 函数 底下有代码讲解
let p1 = new Mypromise(function(resolve,reject){
    resolve('666')
})
p1.then(function(data){
    console.log(data)
},function(err){
    console.log(err)
})

Promise 是一个代理对象,它和原先的异步操作并无关系。它接受一个“执行器 executor”作为参数,我们把原先要执行的异步(并非一定要异步,以后会说明,这里你可以先不深究)操作放进去。

执行器在 Promise 实例创建后立刻开始执行。执行器自带两个参数:resolvereject,这二位都是函数,执行它们会改变 Promise 实例的状态。

Promise 实例有三个状态:

  1. pending [待定] 初始状态,新创建的实例处于这个状态
  2. fulfilled [实现] 操作成功,在执行器里调用 resolve() 之后,实例切换到这个状态
  3. rejected [被否决] 操作失败,在执行器里调用 reject() 之后,实例切换到这个状态

Promise 实例状态改变之后,就会触发后面对应的 .then() 参数里的函数,继续执行后续步骤。另外,Promise 的实例状态只会改变一次,确定为 fulfilledrejected 之一后就不会再变。


我们来看看  Promise函数方面代码要怎么实现:

function Promise(executor) { // executor是一个执行函数
    let self = this;         // 缓存this 防止出错 
    self.status = 'pending';  // 初始化状态
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    function resolve(value) { // 成功状态
        if (self.status === 'pending') {  // 判断当前当前状态
            self.status = 'resolved';  // 把pending 状态变成 成功的状态 resolved
            self.value = value;        // 给默认成功状态 赋值
        }
    }
    function reject(reason) { // 失败状态
        if (self.status === 'pending') { // 判断 Promise 当前状态 如果是初始化 
            self.status = 'rejected';  // pending 状态变成 失败的状态 rejected
            self.reason = reason;  // 给默认失败的原因赋值
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) {   //  捕获执行的时候发生异常,就直接失败了 当 new 一个 新的 Promise对象 抛出一个错误时候
                    // 如  在 Promise实例里  throw new Error('错误了')
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onReject){    let self = this;   
    if(self.status === 'resolved'){ // 
        onFulfilled(self.value)
    }
    if(self.status === 'rejected'){
        onReject(self.reason)
    }
}
module.exports = Promise;

以上是Promise最基本的实现


我们来试试看运行结果: 当Primose 实例状态为 resolve(成功)的时候,显示结果如下




接下来测试下:  当Primose 实例状态为 reject(失败)的时候 显示如下


从代码结果来看,现在Promise 函数 内部的 ‘ pedding’ 状态 已经可以 根据 成功和数百的状态来改变了 

当如果两个函数同时运行呢!!!如下 


只会显示一个结果 谁先执行 状态就变成谁 不会 同时 执行两个结果  

现在我们已经初步实现了 Promise 函数 的同步方法了 

接下来继续 实现其他的 如(多次then ,异步,Promise (then返回一个新的Promise对象))


promise实例可以多次then,当成功后会将 then 中的成功方法按顺序执行,我们可以先讲then中成功的回调存到数组内,当成功是调用成功的数组即可,失败的时候也同理

定义两个接收 成功的数组 和失败的数组 如下:

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    function resolve(value) { // 成功状态
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            // 当成功以后, 依次执行成功数组里面的函数
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }
    function reject(reason) { // 失败状态
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            // 当失败以后, 依次执行失败数组里面的函数
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) { // 捕获的时候发生异常,就直接失败了
        reject(e);
    }
}

然后在 then方法中也需要判断当前的状态   如果是 pending则需要做下处理

Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    if(self.status === 'resolved'){ //
        onFulfilled(self.value)
    }
    if(self.status === 'rejected'){
        onReject(self.reason)
    }
 // 当调用then时可能没成功 也没失败    if (self.status === 'pending') {
        // 此时没有 resolve 也没有 reject
        self.onResolvedCallbacks.push(function(){
            onFulfilled(self.value)
        })
        self.onRejectedCallbacks.push(function(){
            onReject(self.reason)
        })
    }
}

执行实例代码: 结果如下


用了两次 then : 显示都成功了  调用多少次then 就显示多少个成功


链式调用 jquery,jquery能实现链式调用靠的就是返回this,promise不能返回this,promise实现链式调用靠的是返回一个新的promise,如果then中无论是成功的回调还是失败的回调只要返回了结果就会走下一个then中的成功,如果有错误走下一个then的失败

实例代码如下:

let p = new Promise(function(resolve,reject){
    resolve();
})
let p2 = p.then(function(){
    throw new Error('错误');
})
p2.then(function(){  // 可以返回一个新的 Promise 
},function(err){
    console.log(err); // 这边输出错误
})

代码如下:

Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                onFulfilled(self.value);
            }catch (e){
                reject(e)
            }
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                onReject(self.reason)
            }catch (e){
                reject(e)
            }
        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                try{
                    onFulfilled(self.value)
                }catch (e){
                    reject(e)
                }
            });
            self.onRejectedCallbacks.push(function () {
                try{
                    onReject(self.reason)
                }catch (e){
                    reject(e)
                }
            });
        })
    }
    return promise2;
}

如果第一个promise返回一个普通值,会进到下一次then的成功的回调,如果第一个promise返回了一个promise,需要等待返回的promise执行后的结果传递给下一次then中
let p1 = new Promise(function(resolve,reject){
    resolve('333')
})
p1.then(function(data){
    return new Promise(function(resolve,reject){
        setTimeout(function () {
            resolve(100)
        },1000)
    })
},function(err){
    throw new Error('失败')
}).then(function (data) {
    console.log(data)
},function (err) {
    console.log(err)
})


Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x可能是一个promise 也有可能是一个普通的值
            try{
                let x = onFulfilled(self.value);
                // x可能是别人promise,写一个方法统一处理
                resolvePromise(promise2, x, resolve, reject);
            }catch (e){
                reject(e)
            }
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                let x = onRjected(self.reason);
                resolvePromise(promise2, x, resolve, reject);
            }catch (e){
                reject(e)
            }
        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                try{
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                }catch (e){
                    reject(e)
                }
            });
            self.onRejectedCallbacks.push(function () {
                try{
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                }catch (e){
                    reject(e)
                }
            });
        })
    }
    return promise2;
}

接下来实现 resolvePromise 函数

function resolvePromise(promise2, x, resolve, reject) {
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if (promise2 === x) { //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}
快速实现常用的其他 Promise 方法:
// 捕获错误的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}
// 解析全部方法
Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}
// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
// 生成一个成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}
// 生成一个失败的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

写在结尾: 第一次写文章,逻辑有点乱,希望能帮上大家,大家不要喷,有什么错误,请指出,很感谢!肯定会重写写一遍,更好的给大家