Promise of source implementation (perfect accord Promise / A + specification)

 

Original link: point I

Promise is a high frequency front end of the interview in question, when I, as the interviewer, asked Promise probability of more than 90%, far as I know, most companies will ask some questions about the Promise. If you can according to the specifications PromiseA +, write-compliant source code, then I think, for the problems associated Promise interview, are able to give a more perfect answer.

My advice is, write several times to achieve control norms, perhaps the first pass when it is changed several times in order to pass the test, you need to write again, I have the source code for writing to the Promise of no fewer than seven times.

Promise of source code implementation

/ ** 
 * When 1. new Promise, a need to pass executor actuator, the actuator immediately executed 
 * 2. executor accepts two parameters, namely, resolve and Reject 
 * Promise only from pending to 3. Rejected, or from pending to fulfilled 
 state * 4. promise Once confirmed, will not change 
 * 5. promise are then method, then receives two parameters, namely, a successful callback promise onFulfilled, 
 * and promise failed callback onRejected 
 * 6. the call if when the then, promise has succeeded, execution onFulfilled, and the promise of the value passed as parameter. 
 * If the promise has failed, then the implementation of onRejected, the cause of the failure and the promise passed as parameters. 
 * If the status is Pending promise, the need to store up onFulfilled and onRejected function, after the wait state is determined, and then sequentially performing a function corresponding to (Publish-Subscribe) 
 * 7. The parameters of the then onRejected onFulfilled and may default 
 * 8. The promise can then a plurality of times, then the method returns a Promise Promise  
 * If 9. the result is then returned, then this will result as an argument, then passed to the next successful callback (onFulfilled)
 * If then 10. the exception is thrown, then the exception will be passed as a parameter to the next then the failed callback (onRejected)
 * 11. If then returns a promise, you will need to wait for this promise, it will wait for completion of the implementation of this promise, promise if successful, 
 * then went next success, and if that fails, then go to the next failure 
 * / 

PENDING = const 'Pending'; 
const = Fulfilled 'Fulfilled'; 
const = REJECTED 'Rejected'; 
function Promise (Executor) { 
    the let Self = the this; 
    self.status = PENDING; 
    self.onFulfilled = []; // successful callback 
    self.onRejected = []; // failed callback 
    // + 2.1 PromiseA 
    function Resolve (value) { 
        IF (self.status === PENDING) { 
            self.status = Fulfilled;  
            self.value = value;
            self.onFulfilled.forEach (Fn => Fn ( )); // PromiseA + 2.2.6.1 
        } 
    } 

    function Reject (reason) {
        if (self.status === PENDING) {
            self.status = REJECTED;
            self.reason = reason;
            self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2
        }
    }

    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
    let self = this;
    //PromiseA+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
        if (self.status === FULFILLED) {
            //PromiseA+ 2.2.2
            //PromiseA+ 2.2.4 --- setTimeout
            setTimeout(() => {
                try {
                    //PromiseA+ 2.2.7.1
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    //PromiseA+ 2.2.7.2
                    reject(e);
                }
            });
        } else if (self.status === REJECTED) {
            //PromiseA+ 2.2.3
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });
        } else if (self.status === PENDING) {
            self.onFulfilled.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
            self.onRejected.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
        }
    });
    return promise2;
}

function resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    //PromiseA+ 2.3.1
    if (promise2 === x) {
        reject(new TypeError('Chaining cycle'));
    }
    if (x && typeof x === 'object' || typeof x === 'function') {
        let used; //PromiseA+2.3.3.3.3 只能调用一次
        try {
            let then = x.then;
            if (typeof then === 'function') {
                //PromiseA+2.3.3
                then.call(x, (y) => {
                    //PromiseA+2.3.3.1
                    if (used) return;
                    used = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, (r) => {
                    //PromiseA+2.3.3.2
                    if (used) return;
                    used = true;
                    reject(r);
                });

            }else{
                //PromiseA+2.3.3.4
                if (used) return;
                used = true;
                resolve(x);
            }
        } catch (e) {
            //PromiseA+ 2.3.3.2
            if (used) return;
            used = true;
            reject(e);
        }
    } else {
        //PromiseA+ 2.3.3.4
        resolve(x);
    }
}

module.exports = Promise;

There are special test scripts can be written test code compliance PromiseA + specification.

First, the code promise to achieve, add the following code:

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

  

Installation test script:

npm install -g promises-aplus-tests

If the current promise of the source file named promise.js

Then execute the following command in the corresponding directory:

promises-aplus-tests promise.js

promises-aplus-tests in a total of 872 test cases. The above code, perfectly by all use cases.

To do a bit of code to achieve the above brief description of (some other content comments have been written very clearly):

  1. Call onFulfilled and onFulfilled need to put setTimeout, because the specification said: onFulfilled or onRejected must not be called until the execution context stack contains only platform code. Use setTimeout only simulated asynchronous, native Promise is not achieved.

  2. In the function resolvePromise's why need usedd this flag, also because the specification clearly stated: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored so we need to make sure that this flag only once.

  3. self.onFulfilled self.onRejected and stores the callback and the callback failures successful, according to the specification 2.6 show promise from the time when the pending state change, then the need to specify the corresponding callback order.

PromiseA + specification (translated version)

PS: Here is my translation of the specification, for reference

the term

  1. then there is a promise method is a function or object, behavior conforming to this specification
  2. thenable then there is a method of an object or a function
  3. value is the value when the state promise success, including undefined / thenable or promise
  4. exception is a use value throw an exception thrown
  5. reason is the value when the failed state promise

Claim

2.1 Promise States

Promise must be in one of three states: pending, fulfilled or is rejected

2.1.1 If the promise in pending state
2.1.1.1 可以变成 fulfilled 或者是 rejected
2.1.2 If the promise fulfilled in state
2.1.2.1 不会变成其它状态

2.1.2.2 必须有一个value值
2.1.3 If the promise in the state rejected
2.1.3.1 不会变成其它状态

2.1.3.2 必须有一个promise被reject的reason

That generalization is: promise fulfilled from the state can only become pending, or become rejected.promise success from pending, a successful value.promise fails, there are reasons for failure

2.2 then the method

promise must then provide a way to access the final result

The method then receives promise two parameters

promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled parameters are optional and onRejected
2.2.1.1 onFulfilled 必须是函数类型

2.2.1.2 onRejected 必须是函数类型
2.2.2 If onFulfilled is a function of:
2.2.2.1 必须在promise变成 fulfilled 时,调用 onFulfilled,参数是promise的value
2.2.2.2 在promise的状态不是 fulfilled 之前,不能调用
2.2.2.3 onFulfilled 只能被调用一次
2.2.3 If onRejected is a function of:
2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason
2.2.3.2 在promise的状态不是 rejected 之前,不能调用
2.2.3.3 onRejected 只能被调用一次
2.2.4 onFulfilled and onRejected should be micro-task
2.2.5 onFulfilled and onRejected must be called as a function
2.2.6 then the method may be called multiple times
2.2.6.1 如果promise变成了 fulfilled态,所有的onFulfilled回调都需要按照then的顺序执行
2.2.6.2 如果promise变成了 rejected态,所有的onRejected回调都需要按照then的顺序执行
2.2.7 then must return a promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 执行的结果为x,调用 resolvePromise
2.2.7.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e,promise2需要被reject
2.2.7.3 如果 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
2.2.7.4 如果 onRejected 不是一个函数,promise2 以promise1的reason rejected

2.3 resolvePromise

resolvePromise(promise2, x, resolve, reject)

2.3.1 If promise2 and x are equal, then reject promise with a TypeError
2.3.2 If x is a promsie
2.3.2.1 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
2.3.2.2 如果 x 被 fulfilled, fulfill promise with the same value.
2.3.2.3 如果 x 被 rejected, reject promise with the same reason.
2.3.3 If x is an object or a function
2.3.3.1 let then = x.then.
2.3.3.2 如果 x.then 这步出错,那么 reject promise with e as the reason..
2.3.3.3 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
    2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
    2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
    2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
    2.3.3.3.4 如果调用then抛出异常e 
        2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
        2.3.3.3.4.3 否则,reject promise with e as the reason
2.3.3.4 如果 then 不是一个function. fulfill promise with x.
2.3.4 If x is not an object or function, fulfill promise with x.

Other methods of Promise

Although the above promise PromiseA + source specifications have been met, but the native Promise also provides a number of other methods, such as:

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.catch()
  4. Promise.prototype.finally()
  5. Promise.all()
  6. Promise.race()

The following specific about the realization of each method:

Promise.resolve

Promise.resolve (value) returns a Promise to send the object after a given value resolution.

  1. If the value is thenable objects, return the promise will "follow" the object of this thenable, using its final state
  2. If the incoming value itself is a promise object, then Promise.resolve will not do any modification, unchanged promise to return this object.
  3. Otherwise, it returns directly to the target value promise success state.
Promise.resolve = function (param) {
        if (param instanceof Promise) {
        return param;
    }
    return new Promise((resolve, reject) => {
        if (param && param.then && typeof param.then === 'function') {
            setTimeout(() => {
                param.then(resolve, reject);
            });
        } else {
            resolve(param);
        }
    });
}

SetTimeout reason for performing addition of thenable object is inferred based on the results of the executed native Promise objects, the following test code to native execution results: 2,040,030; same sequential execution order, setTimeout delay increases.

Test code:

let p = Promise.resolve(20);
p.then((data) => {
    console.log(data);
});


let p2 = Promise.resolve({
    then: function(resolve, reject) {
        resolve(30);
    }
});

p2.then((data)=> {
    console.log(data)
});

let p3 = Promise.resolve(new Promise((resolve, reject) => {
    resolve(400)
}));
p3.then((data) => {
    console.log(data)
});

  

 

Promise.reject

Promise.reject Promise.resolve methods and different parameters Promise.reject () method, as a reason to reject will be intact, the parameters of the subsequent process becomes.

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

  

 

Promise.prototype.catch

Promise.prototype.catch used to specify a callback when an error occurs, then a special method, after the catch, can continue .then

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}

Promise.prototype.finally

Regardless of success or failure, will finally come in, and then finally, you can continue then. And values ​​are passed intact to the back of then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}

Promise.all

Promise.all (promises) Returns a promise objects

  1. If the argument passed is an empty iterable, then the promise callback object is completed (resolve), only in this case, the synchronization is performed, the other is asynchronous returned.
  2. If the incoming parameter does not contain any promise, it returns an asynchronous completion.
  3. Callback completed when promises are all promise promise are "done" or parameter does not contain a promise.
  4. If the parameter has a promise fails, then the promise of failure to return the object Promise.all
  5. In any case, the result of the completion status of promise is to return an array Promise.all
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                  //promises[i] 可能是普通值
                  Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

Test code:

var promise1 = new Promise((resolve, reject) => {
    resolve(3);
})
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); //[3, 42, 'foo']
},(err)=>{
    console.log(err)
});

var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
    console.log('the stack is now empty');
    console.log(p2);
});

Promise.race

Promise.race function returns a Promise, promise to complete it in the same manner as the first pass is completed. It can be completed (resolves), can also be a failure (rejects), depending on the completion of the first two is the way in which.

If you pass a parameter array is empty, the returned promise will wait forever.

If the iteration contains one or more non-commitment to values ​​and / or commitment has been resolved / rejected, the Promise.race resolves a value for the first iteration finds.

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            return;
        } else {
            for (let i = 0; i < promises.length; i++) {
                Promise.resolve(promises[i]).then((data) => {
                    resolve(data);
                    return;
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

Test code:

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    undefined,
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log('success ', data);
}, (err) => {
    console.log('err ',err);
});

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
});

Guess you like

Origin www.cnblogs.com/myfate/p/11122231.html