promise引见--完成篇

本日,我带着人人一步一步随着范例完成一个自身的Promise,人人能够对比我的第二篇文章Promise引见–范例篇或官方范例来逐一进修。

Promise内部有三个牢固的状况,我们在文件中提前定义。

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

起首,是组织器constructor

   constructor(resolver){
        this._status = PENDING; // 保留内部的状况
        this._result = undefined; // 保留promise对象fulfill或reject的终究效果 
        this._childArr = []; // 挪用then要领建立的子promise对象
        this._fulfillArr = []; // 挪用then要领增加的onFulfilled要领
        this._rejectArr = []; // 挪用then要领增加的onRejected要领

        if (resolver == noop) { return}; // then要领内部建立promise时候运用

        // 假如resolver不是函数,则抛出TypeError毛病
        if (!isFunction(resolver)) {
            throw new TypeError('参数必需为function');
        };
        // 假如直接挪用Promise而非经由过程new关键词建立,一样抛出TypeError毛病
        if (this instanceof Promise) {
            initPromise(this, resolver)
        } else {
            throw new TypeError('Promise不能够直接作为函数挪用')
        }
    }

当参数通报准确时,才真正初始化Promise对象,我提到了一个零丁的函数initPromise中,以下:

function initPromise(promise, resolver){
    // 当挪用传入的resolver函数抛出非常,则reject当前promise
    try {
        resolver(function(value){
            // 封装的内部函数,处置惩罚resolve(value),也就是Promise Resolution Procedure
            resolve(promise, value);
        }, function(reason){
            // 封装的内部函数,处置惩罚reject(reason)
            reject(promise, reason);
        });
    } catch (e) {
        reject(promise, e);
    }
}

当我们实行new Promise((resolve){resolve(5)})时,会走resolve(promise, value)。接下来我们完成一下Promise Resolution Procedure

function resolve(promise, value){
    // 2.3.1 假如promise和value指向统一对象
    if (promise === value) {
        reject(promise, new TypeError('不能够resolve Promise实例自身'))
    // 2.3.2 假如value是一个promise
    } else if (value instanceof Promise) {
        // 2.3.2.2 假如value处于fulfilled状况,则运用雷同的value值fulfill promise。
        if (value._status == FULFILLED) {
            fulfill(promise, value._result);
        // 2.3.2.3 假如value处于rejected状况,则运用雷同的reason值reject promise。
        } else if (value._status == REJECTED) {
            reject(promise, value._result);
        // 2.3.2.1 假如value处于pending状况,则promise一样pending并直到value状况转变。
        // 从新把resolve(promise, value)增加到行列,asyncCall封装了一下异步挪用
        } else {
            asyncCall(resolve, [promise, value]);
        }
    // 2.3.3 假如x是一个object或function
    } else if (isObjectOrFunction(value)){
        // 2.3.3.2 假如猎取value.then的值时抛出非常,则经由过程该非常reject promise
        try{
            let then = value.then; // 2.3.3.1 使then即是value.then
            // 2.3.3.3 假如then是一个函数
            if (isFunction(then)) {
                try{
                    handleThenable(promise, value, then);
                } catch (e) {
                    reject(promise, e);
                }
            // 2.3.3.4 假如then不是一个函数
            } else {
                fulfill(promise, value);
            }
        } catch (e) {
            reject(promise, e);
        }
    // 2.3.4 value不是对象或函数
    } else {
        fulfill(promise, value);
    }
}

由于value.then是函数时,处置惩罚状况一样许多且比较芜杂,我零丁把这部份处置惩罚提取到handleThenable函数中。完成以下:

function handleThenable(promise, value, then){
    let settled = false; // 是不是fulfilled或rejected
    try {
        // 2.3.3.3 假如then是一个函数,则把value作为函数中this指一向挪用它
        then.call(value, (otherValue)=>{
            // 2.3.3.3.3 
            if (settled) { return};
            // 2.3.3.3.1 假如resolvePromise经由过程传入y来挪用,则实行resolve(promise, y)
            resolve(promise, otherValue);
            settled = true;
        }, (reason)=>{
            // 2.3.3.3.3 
            if (settled) { return};
            // 2.3.3.3.2 假如rejectPromise 经由过程传入缘由r来挪用,则传入r来reject promise
            reject(promise, reason);
            settled = true;
        })
    // 2.3.3.3.4 假如挪用then抛出非常e
    } catch (e) {
        // 2.3.3.3.4.1 假如resolvePromise或rejectPromise已挪用,则疏忽
        if (settled) { return};
        settled = true;
        // 2.3.3.3.4.2 不然,则传入e来reject promise
        reject(promise, e)
    }
}

以上,基础就是完全的我对Promise Resolution Procedure的完成。
另有一个非常主要的要领就是then要领,接下来我们看一下它是怎样完成的。then内部总体上分为两种状况,一种是当前promise对象状况已变成fulfilledrejected,此时则直接把响应的回调函数增加到异步行列中,另一种状况是当前promise对象状况照样pending,此时则把响应的回调函数顺次增加到数组中。

then(onFulfilled, onRejected){
    let child = new Promise(noop);
    // 假如当前对象状况已转变,则直接依据状况挪用响应的回调
    if (this._status !== PENDING) {
        if (this._status == FULFILLED) {
            // 2.2.4 异步挪用
            asyncCall(()=>{
                dealThen(this, child, onFulfilled);
            })
        } else {
            // 2.2.4 异步挪用
            asyncCall(()=>{
                dealThen(this, child, onRejected);
            })
        }
    // 假如当前对象处于pending状况,则把onFulfilled, onRejected增加到
    } else {
        this._childArr.push(child);
        this._fulfillArr.push(onFulfilled);
        this._rejectArr.push(onRejected);
    }
    // 返回一个新的promise对象
    return child;
}

详细处置惩罚逻辑我放到了一个新的函数dealThen中,注重它是异步挪用的。所以用asyncCall要领包装了一下。

// 处置惩罚then
function dealThen(promise, child, x){
    // onFulfilled或onRejected是一个函数
    if (isFunction(x)) {
        // 2.2.7.1 假如onFulfilled或onRejected返回了一个值value,则实行resolve(child, value)
        try {
            resolve(child, x(promise._result));
        // 2.2.7.2 假如onFulfilled或onRejected抛出了非常e,则reject child并传入缘由e
        } catch (e) {
            reject(child, e);
        }
    } else {
        try{
            // 2.2.1.1 假如onFulfilled不是一个函数,则疏忽
            if (promise._status == FULFILLED) {
                fulfill(child, promise._result);
            // 2.2.1.2 假如onRejected不是一个函数,则疏忽
            } else {
                reject(child, promise._result);
            }
        } catch (e) {
            reject(child, e);
        }
    }
}

从上面的代码中我们看到有两个比较主要的要领——fulfillreject,它们才是真正转变promise状况并挪用响应回调的处所。

function fulfill(promise, value){
    // 假如状况已不是pending,则直接return
    if (promise._status !== PENDING) { return };
    // 设置状况为fulfilled,并设置终究效果
    promise._status = FULFILLED;
    promise._result = value;
    // 异步顺次挪用增加的onFulfilled要领
    if (promise._fulfillArr.length > 0) {
        // 2.2.6.1 假如promise fulfilled,则一切的onFulfilled回调函数根据它们增加的递次顺次挪用。
        promise._fulfillArr.forEach((k,index)=>{
            // 2.2.5 onFulfilled和onRejected必需作为函数来挪用,没有this值
            asyncCall(dealThen, [promise, promise._childArr[index], k])
        });
    }
}

function reject(promise, reason){
    // 假如状况已不是pending,则直接return
    if (promise._status !== PENDING) { return };

    // 设置状况为rejected,并设置终究效果
    promise._status = REJECTED;
    promise._result = reason;

    // 异步顺次挪用增加的onRejected要领
    if (promise._rejectArr.length > 0) {
        // 2.2.6.2 假如promise rejected,则一切的onRejected回调函数根据它们增加的递次顺次挪用。
        promise._rejectArr.forEach((k,index)=>{
            // 2.2.5 onFulfilled和onRejected必需作为函数来挪用,没有this值
            asyncCall(dealThen, [promise, promise._childArr[index], k])
        });
    }
}

固然,另有一个实例要领catch,实在它挪用的也是then要领。

catch(onRejected){
    return this.then(undefined, onRejected);
}

固然,Promise另有四个实例要领,离别以下:

resolve

Promise.resolve = function(value){
    return new Promise(function(resolve){resolve(value)})
}

reject

Promise.reject = function(reason){
    return new Promise(function(resolve, reject){reject(reason)})
}

allrace的完成没有太好好思索,也没有跑测试,不知道有无题目,而且个人感觉完成的也比较挫,是用setInterval一向轮询检察每个promise实例状况有无转变,所以就不show code了。主要内容照样讲Promises/A+的完成。

完全的代码见我的github

固然也有几个题目,异步我是用setTimeout完成的,它属于macro-task,而原生的Promise属于micro-task,所以这里另有待革新。

别的,在上面的完成中,我们发明resolve(promise, value)中,在对2.3.2.1 假如value处于pending状况,则promise一样pending并直到value状况转变。我基础采纳的也是setTimeout
轮询的体式格局完成的。以后看了es-promise的完成,由于依据2.3.2.1此时promise的状况和value是同步的,所以能够把resolvereject promise离别放在value响应状况的回调中,并假定此时与之对应的valuechildundefined。以下所示:

// asyncCall(resolve, [promise, value]);
value._childArr.push(undefined);
value._fulfillArr.push((value)=>{resolve(promise, value)}); // ①
value._rejectArr.push((reason)=>{reject(promise, reason)}); // ②

此时我们还需要对fulfillreject两个要领稍作修改。

function fulfill(promise, value){
    if (promise._status !== PENDING) { return };

    promise._status = FULFILLED;
    promise._result = value;

    if (promise._fulfillArr.length > 0) {
        promise._fulfillArr.forEach((k,index)=>{
            // 假如对应的child不是undefined,则异步挪用回调
            if (promise._childArr[index]) {
                asyncCall(dealThen, [promise, promise._childArr[index], k])
            // 假如对应的child是undefined,则直接实行回调函数①并传入value
            } else {
                k(value);
            }
        });
    }
}

function reject(promise, reason){
    if (promise._status !== PENDING) { return };

    promise._status = REJECTED;
    promise._result = reason;

    if (promise._rejectArr.length > 0) {
        promise._rejectArr.forEach((k,index)=>{
            // 假如对应的child不是undefined,则异步挪用回调
            if (promise._childArr[index]) {
                asyncCall(dealThen, [promise, promise._childArr[index], k])
            // 假如对应的child是undefined,则直接实行回调函数②并传入reason
            } else {
                k(reason);
            }
        });
    };
    
}

修改版见Promise1

毛病或不足之处,迎接斧正。

    原文作者:武官尚书
    原文地址: https://segmentfault.com/a/1190000007765809
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞