写一个符合promiseA+规范的promise实现

如何写一个符合promiseA+规范的promise实现

前言

Promise 是异步编程的一种解决方案:
从语法上讲,promise是一个对象,从它能够获取异步操做的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会当即执行。

编写符合promiseA+规范的promise实现

在实现以前,能够先看一下Promise A plus规范jquery

1. 建立promise构造函数


这里先实现promise最基本的功能:promise建立后当即执行;在then时执行相应的函数;捕获错误当即变成reject态npm

// promise里只有一个参数,叫executor(执行器)
function Promise(executor) {
    let self = this;
    self.status = 'pending';//等待态
    self.value = undefined;//默认成功的值
    self.err = undefined;//默认失败的值
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
        }
    }
    function reject(err) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.err = err;
        }
    }
    try {//捕获时发生异常,直接变成reject态,抛出错误
        executor(resolve, reject);//promise实例建立后,当即执行
    } catch (error) {
        reject(error);  
    }
}
//在prototype上定义then实例方法
Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    if (self.status === 'resolved') {
        onFulfilled(self.value);
    }
    if (self.status === 'rejected') {
        onRejected(self.err);
    }
}

这里咱们先测试一下咱们的Promise
图片描述编程

这里便实现了基本功能,前面说过Promise 是异步编程的一种解决方案
咱们加个异步逻辑运行一下:数组

图片描述

2. Promise异步调用


咱们都知道异步代码并不会当即执行,这时既不是resolved也不是rejected,而是 pending
在以前的状态判断里面,正好丢了一个pending状态。OK,这时须要在then里判断当status为pending时,先将onFulfilled, onRejected存入数组里,当status改变时,再遍历数组让里面的函数依次执行,看代码。

(1)申明两个存放onFulfiled,onRejected的数组promise

function Promise(resolver) {
    let self = this;
    self.status = 'pending';//等待态
    self.value = undefined;//默认成功的值
    self.err = undefined;//默认失败的值
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            self.onResolvedCallbacks.forEach(fn=>{//调用resolve时,依次执行数组里的函数
                fn();
            })
        }
    }
    function reject(err) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.err = err;
            self.onRejectedCallbacks.forEach(fn=>{
                fn();
            })
        }
    }
    try {//捕获时发生异常,直接抛出错误
        resolver(resolve, reject);//promise实例建立后,当即执行它的方法
    } catch (error) {
        reject(error)
    }
}

(2)接着在then方法里添加pending的判断异步

Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    if (self.status === 'resolved') {
        onFulfilled(self.value);
    }
    if (self.status === 'rejected') {
        onRejected(self.err);
    }
    if(self.status==='pending'){// 此时没resolved,也没rejectd
        self.onResolvedCallbacks.push(()=>{
            onFulfilled(self.value);
        });
        self.onRejectedCallbacks.push(()=>{
            onRejected(self.err);
        })
    }
}

再看刚刚的异步逻辑
图片描述异步编程

1s后就执行成功了,是否是很神奇,再看下面:函数

3. Promise链式调用


(1)规范里说在同一个promise里then能够被屡次调用。测试

图片描述

(2)jquery能实现链式调用靠的是返回this,而promise不能返回this,规范里又说它返回的是一个新的Promise实例(注意,不是原来那个Promise实例)ui

图片描述

在then里新建一个promise2并为每个状态包一个Promise

图片描述

写到这里,再来看看规范,规范里说道
(1)x多是一个promise;

图片描述

(2)多是一个对象或者方法;

图片描述

(3)也有多是一个普通的值

图片描述

这时须要一个方法来处理x

3.1 对onFulfilled和onRejected的返回值进行处理


因而引入一个处理方法resolvePromise(promise2, x, resolve, reject);
这里须要注意一下,有些人写的promise可能会既调用成功,又调用失败,若是两个都调用先调用谁另外一个就忽略掉。
因而增长一个判断called表示是否调用过成功或者失败,看代码:

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {//promise2和x不能相同
        return reject(new TypeError('循环引用了'))
    }
    let called;// 表示是否调用过成功或者失败
    //这里对x的类型进行判断
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {  // 判断x是否是promise,若是x是对象而且x的then方法是函数咱们就认为他是一个promise
            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); // 表示成功了
    }
}

相应的将前面的代码进行一些更改
图片描述

4. 值的穿透问题


若是在then中什么都不传,值会穿透到最后调用的时候;
图片描述

这时须要在then里给onFulfilled和onRejected写一个默认的函数

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {
        throw err;//这里须要抛出错误,不能return err,不然会在下一次调用成功态
    }

5. then的异步实现


规范里要求,全部的onFulfilled和onRejected都要确保异步执行

图片描述

这里以resolve为例,写一个setTimeout():
图片描述

6. defer语法糖


在使用promise的过程当中,咱们都须要先new Promise(),好比说:

function read() {
    let fs = require('fs');
    let promise = new Promise(function(resolve,reject){
        fs.readFile('./1.txt','utf8',function(err,data){
            if(err) reject(err);
            resolve(data);
        })
    });
    return promise
}

在Promise中,它为咱们提供了一个语法糖Promise.defer,用Promise.defer只需这样写:

function read() {
    let defer = Promise.defer()
    require('fs').readFile('.//1.txt', 'utf8', function (err, data) {
        if(err) defer.reject(err);
        defer.resolve(data);
    })
    return defer.promise;
}

为此,再为咱们的Promise加一个defer方法:

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

在这里,咱们基本实现了一个比较完整的promise;固然Promise还有许多静态方法,还有js的异步发展史,这些能够在下一次进行讨论。
完整代码:

// promise里只有一个参数,叫executor(执行器)
function Promise(executor) {
    let self = this;
    self.status = 'pending';//等待态
    self.value = undefined;//默认成功的值
    self.err = 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(err) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.err = err;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }
    try {//捕获时发生异常,直接变成reject态,抛出错误
        executor(resolve, reject);//promise实例建立后,当即执行
    } catch (error) {
        reject(error);  
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {//promise2和x不能相同
        return reject(new TypeError('循环引用了'))
    }
    let called;// 表示是否调用过成功或者失败
    //这里对x的类型进行判断
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {  // 判断x是否是promise,若是x是对象而且x的then方法是函数咱们就认为他是一个promise
            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); // 表示成功了
    }
}
//在prototype上定义then实例方法
Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {
        throw err;//这里须要抛出错误,不能return err,不然会在下一次调用成功态
    }
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRejected(self.err);
                    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 () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            self.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRejected(self.err);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

module.exports = Promise;

7.Promise测试


npm i -g promises-aplus-tests
promises-aplus-tests Promise.js
相关文章
相关标签/搜索