Promise简介

是一个保留了异步事件将来执行后果的对象。它不是一个新技术,而是一种能够优化异步编程编码格调的标准。最早诞生于社区,用于解决JavaScript代码中回调嵌套层级过多的问题和错误处理逻辑堆砌的问题。应用Promise对象封装异步操作,能够让代码变得直观、线性、优雅。

Promise根本应用

用Promise封装Ajax申请

//先写一个原始的Ajax申请let xhr = new XMLHttpRequest()function resolve(v){console.log(v);}function reject(e){console.log(e);}xhr.onerror = function(e){    reject(e)}xhr.ontimeout = function(e){    reject(e)}xhr.onreadystatechange = function(){    if(xhr.readyState===4){        if(xhr.status===200){            resolve(xhr.response)        }    }}xhr.open('Get','https://wwww.google.com',true)xhr.send()// 第一版:利用Promise封装ajaxlet p = new Promise((resolve,reject)=>{    let xhr = new XMLHttpRequest()    xhr.onerror = function(e){        reject(e)    }    xhr.ontimeout = function(e){        reject(e)    }    xhr.onreadystatechange = function(){        if(xhr.readyState===4){            if(xhr.status===200){                resolve(xhr.response)            }        }    }    xhr.open('Get','https://wwww.google.com',true)    xhr.send()});p.then(v=>{    console.log("我是胜利时注册的回调");},e=>{    console.log("我是失败时注册的回调");})// 第二版 反对传参、封装了Promise创立的细节function Xpromise(request){    function executor(request,resolve,reject){        let xhr = new XMLHttpRequest()        xhr.onerror = function(e){            reject(e)        }        xhr.ontimeout = function(e){            reject(e)        }        xhr.onreadystatechange = function(){            if(xhr.readyState===4){                if(xhr.status===200){                    resolve(xhr.response)                }            }        }        xhr.open('Get',request.url,true)        xhr.send()    }    renturn new Promise(executor);}let x1 = Xpromise(makeRequest('https://wwww.google.com')).then(v=>{    console.log("我是胜利时注册的回调");},e=>{    console.log("我是失败时注册的回调");})

另外,Promise还提供了一系列好用的API,如动态resolve()、all()、race()办法等。

实现原理概述

Promise用回调函数提早绑定回调函数onResolve返回值穿透机制解决回调嵌套层级过多的问题;应用谬误冒泡机制简化了错误处理逻辑堆砌的问题。

手工实现一个Promise

第一版

// 第一点:Promise是一个类class MyPromise {    // 第二点:Promised构造函数的参数是一个函数;    constructor(fn) {        if (typeof fn !== "function") {            throw new Error("promise的结构函数参数应该为函数类型")        }        // 第三点:Promise的外部状态有三个,Promise对象具备值        this._status = PENDING;        this._value = undefined;        // 第五点:new Promise(fn)时,就须要执行fn做业务逻辑,故构造函数里就要调用fn函数。此处外部函数_resolve和_reject会被调用用于追踪Promise的外部状态        try {            //留神用try-catch包住            fn(this._resolve.bind(this), this._reject.bind(this));        } catch (err) {            this._reject(err)        }    }    // 第四点:定义MyPromise状态翻转时,要执行的外部函数    _resolve(val){        if (this._status !== this.PENDING) return //这段代码体现了Promise的状态翻转:只能是P->F或者是P->R        this._status = FULLFILLED;        this._value = val;    };    _reject(err){        if (this._status !== this.PENDING) return        this._status = REJECTED;        this._value = err;    };}

第二版

class MyPromise {    constructor(fn) {        if (typeof fn !== "function") {            throw new Error("myPromise的结构函数参数应该为函数类型")        }        this._status = PENDING;        this._value = undefined;        //个性2-新增定义两个回调函数数组        this.fulfilledQueue = [];        this.rejectedQueue = [];        try {            fn(this._resolve.bind(this), this._reject.bind(this));        } catch (err) {            this._reject(err)        }    }    _resolve(val){        if (this._status !== this.PENDING) return        // 个性4:注册的回调函数在Promise状态翻转时会执行,执行的形式是循环从队列外面取出回调执行        // 定义run函数        run = (value)=>{            this._status = FULLFILLED;            this._value = val;            let ck;            while(ck = this.fulfilledQueue.shift()){                ck(value);            }        }        // 个性5:run()函数的执行,这里十分要害。要把run放进setTimeOut。为什么?        // 因为执行_resolve()函数时,then()可能还没执行,所以为了让then()中的回调、包含链式调用的then()的回调增加到fulfilledQueue中,        // 须要提早执行run()。实际上这里用setTimeout性能差,理论中采纳微工作的形式实现        setTimeout(run,0)        //run();    };    _reject(err){        if (this._status !== this.PENDING) return        run = (error)=>{            this._status = this.REJECTED;            this._value = err;            let ck;            while(ck = this.rejectedQueue.shift()){                ck(error)            }        }        setTimeout(run,0);    };    // 最重要的then函数:    // 个性1-用于注册回调,then()函数有两个参数,都是可选的,如果参数不是函数将会被疏忽    // then()反对被同一个Promise屡次注册,个性2-所以Promise外部要保护两个数组,别离存储then上注册的胜利回调和失败回调);    // then()反对链式调用,个性3-之所以反对是因为其返回值是一个新的Promise,此处要实现回调函数onFulfilled穿透机制的实现        //个性1-回调函数的注册:故为它传递两个回调函数占位    then(onFulfilled,onRejected){        const {_status, _value} = this;        //个性3-返回一个新的promise        return new MyPromise((onFulfilledNext, onRejectedNext)=>{            let fulfilled = value => {                try{                    if(typeof onFulfilled != "function"){                        //如果 onFulfilled 或 onRejected 不是函数,onFulfilled 或 onRejected 被疏忽                        onFulfilledNext(value)                    }else{                        //如果 onFulfilled 或者 onRejected 返回一个值 res                        let res = onFulfilled(value)                        if(res instanceof MyPromise){                            //若 res 为 Promise ,这时后一个回调函数,就会期待该 Promise 对象(即res )的状态发生变化,才会被调用,并且新的 Promise 状态和 res 的状态雷同                            res.then(onFulfilledNext, onRejectedNext)                        }else{                            //若 res 不为 Promise ,则使res 间接作为新返回的 Promise 对象的值                            onFulfilledNext(res)                        }                    }                }catch(err){                    onRejectedNext(err)                }            }            let rejected = error => {                try{                    if(typeof onRejected != "function"){                        onRejectedNext(error)                    }else{                        let res = onRejectedNext(error)                        if(res instanceof MyPromise){                            res.then(onFulfilledNext, onRejectedNext)                        }else{                            onFulfilledNext(res);                        }                    }                }catch(err){                    onRejectedNext(err)                }            }            switch(_status){                case PENDING:                    //在 promise 状态扭转前, onFulfilled或者onRejected不可被调用                    this._fulfilledQueue.push(onFulfilled)                    this._rejectedQueue.push(onRejected)                    break                case FULFILLED:                    //onFulfilled调用次数只有一次,fulfilled对onFulFilled进行包装。why need 包装?因为onFulFilled可能是函数、可能不是,如果是函数,返回值可能是Promise可能不是                    fulfilled(_value)                    break                case REJECTED:                    //onRejected调用次数只有一次                    rejected(_value)                    break            }        })    }}

第三版

class MyPromise {    constructor(handle) {        if (!isFunction(handle)) {            throw new Error('MyPromise must accept a function as a parameter')        }        this._status = PENDING        this._value = undefined        this._fulfilledQueues = []        this._rejectedQueues = []        try {            handle(this._resolve.bind(this), this._reject.bind(this))        } catch (err) {            this._reject(err)        }    }    // 增加resovle时执行的函数    _resolve(val) {        const run = () => {            if (this._status !== PENDING) return            const runFulfilled = (value) => {                let cb;                while (cb = this._fulfilledQueues.shift()) {                    cb(value)                }            }            const runRejected = (error) => {                let cb;                while (cb = this._rejectedQueues.shift()) {                    cb(error)                }            }            /*               如果resolve的参数为Promise对象,则必须期待该Promise对象状态扭转后,              以后Promsie的状态才会扭转,且状态取决于参数Promsie对象的状态,              因而这里进行逻辑辨别            */            if (val instanceof MyPromise) {                val.then(value => {                    this._value = value                    this._status = FULFILLED                    runFulfilled(value)                }, err => {                    this._value = err                    this._status = REJECTED                    runRejected(err)                })            } else {                this._value = val                this._status = FULFILLED                runFulfilled(val)            }        }        setTimeout(run, 0)    }    _reject(err) {        if (this._status !== PENDING) return        const run = () => {            this._status = REJECTED            this._value = err            let cb;            while (cb = this._rejectedQueues.shift()) {                cb(err)            }        }        setTimeout(run, 0)    }    then(onFulfilled, onRejected) {        const { _value, _status } = this        return new MyPromise((onFulfilledNext, onRejectedNext) => {            let fulfilled = value => {                try {                    if (!isFunction(onFulfilled)) {                        onFulfilledNext(value)                    } else {                        let res = onFulfilled(value);                        if (res instanceof MyPromise) {                            res.then(onFulfilledNext, onRejectedNext)                        } else {                            onFulfilledNext(res)                        }                    }                } catch (err) {                    onRejectedNext(err)                }            }            let rejected = error => {                try {                    if (!isFunction(onRejected)) {                        onRejectedNext(error)                    } else {                        let res = onRejected(error);                        if (res instanceof MyPromise) {                            res.then(onFulfilledNext, onRejectedNext)                        } else {                            onFulfilledNext(res)                        }                    }                } catch (err) {                    onRejectedNext(err)                }            }            switch (_status) {                case PENDING:                    this._fulfilledQueues.push(fulfilled)                    this._rejectedQueues.push(rejected)                    break                case FULFILLED:                    fulfilled(_value)                    break                case REJECTED:                    rejected(_value)                    break            }        })    }}

第四版 finally

class MyPromise {    constructor(handle) {        if (!isFunction(handle)) {            throw new Error('MyPromise must accept a function as a parameter')        }        this._status = PENDING        this._value = undefined        this._fulfilledQueues = []        this._rejectedQueues = []        try {            handle(this._resolve.bind(this), this._reject.bind(this))        } catch (err) {            this._reject(err)        }    }    _resolve(val) {        const run = () => {            if (this._status !== PENDING) return            const runFulfilled = (value) => {                let cb;                while (cb = this._fulfilledQueues.shift()) {                    cb(value)                }            }            const runRejected = (error) => {                let cb;                while (cb = this._rejectedQueues.shift()) {                    cb(error)                }            }            if (val instanceof MyPromise) {                val.then(value => {                    this._value = value                    this._status = FULFILLED                    runFulfilled(value)                }, err => {                    this._value = err                    this._status = REJECTED                    runRejected(err)                })            } else {                this._value = val                this._status = FULFILLED                runFulfilled(val)            }        }        setTimeout(run, 0)    }    _reject(err) {        if (this._status !== PENDING) return        const run = () => {            this._status = REJECTED            this._value = err            let cb;            while (cb = this._rejectedQueues.shift()) {                cb(err)            }        }        setTimeout(run, 0)    }    then(onFulfilled, onRejected) {        const { _value, _status } = this        return new MyPromise((onFulfilledNext, onRejectedNext) => {            let fulfilled = value => {                try {                    if (!isFunction(onFulfilled)) {                        onFulfilledNext(value)                    } else {                        let res = onFulfilled(value);                        if (res instanceof MyPromise) {                            res.then(onFulfilledNext, onRejectedNext)                        } else {                            onFulfilledNext(res)                        }                    }                } catch (err) {                    onRejectedNext(err)                }            }            let rejected = error => {                try {                    if (!isFunction(onRejected)) {                        onRejectedNext(error)                    } else {                        let res = onRejected(error);                        if (res instanceof MyPromise) {                            res.then(onFulfilledNext, onRejectedNext)                        } else {                            onFulfilledNext(res)                        }                    }                } catch (err) {                    onRejectedNext(err)                }            }            switch (_status) {                case PENDING:                    this._fulfilledQueues.push(fulfilled)                    this._rejectedQueues.push(rejected)                    break                case FULFILLED:                    fulfilled(_value)                    break                case REJECTED:                    rejected(_value)                    break            }        })    }    // 增加catch办法    catch(onRejected) {        return this.then(undefined, onRejected)    }    // 增加动态resolve办法    static resolve(value) {        // 如果参数是MyPromise实例,间接返回这个实例        if (value instanceof MyPromise) return value        return new MyPromise(resolve => resolve(value))    }    // 增加动态reject办法    static reject(value) {        return new MyPromise((resolve, reject) => reject(value))    }    // 增加动态all办法    static all(list) {        return new MyPromise((resolve, reject) => {            /**             * 返回值的汇合             */            let values = []            let count = 0            for (let [i, p] of list.entries()) {                // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve                this.resolve(p).then(res => {                    values[i] = res                    count++                    // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled                    if (count === list.length) resolve(values)                }, err => {                    // 有一个被rejected时返回的MyPromise状态就变成rejected                    reject(err)                })            }        })    }    // 增加动态race办法    static race(list) {        return new MyPromise((resolve, reject) => {            for (let p of list) {                // 只有有一个实例率先扭转状态,新的MyPromise的状态就跟着扭转                this.resolve(p).then(res => {                    resolve(res)                }, err => {                    reject(err)                })            }        })    }    finally(cb) {        return this.then(            value => MyPromise.resolve(cb()).then(() => value),            reason => MyPromise.resolve(cb()).then(() => { throw reason })        );    }}

其它问题

1. Promise与宏观工作、async函数等执行程序问题

Promise是微工作的一种实现,给出如下的代码,剖析其输入程序

//题目1async function a1 () {    console.log('a1 start')    await a2()    console.log('a1 end')}async function a2 () {    console.log('a2')}console.log('script start')setTimeout(() => {    console.log('setTimeout')}, 0)Promise.resolve().then(() => {    console.log('promise1')})a1()let promise2 = new Promise((resolve) => {    resolve('promise2.then')    console.log('promise2')})promise2.then((res) => {    console.log(res)    Promise.resolve().then(() => {        console.log('promise3')    })})console.log('script end')//题目2async function async1() {    console.log('async1 start');    await async2();    await async3()    console.log('async1 end');}async function async2() {    console.log('async2');}async function async3() {    console.log('async3');}console.log('script start');setTimeout(function() {    console.log('setTimeout');}, 0)async1();new Promise(function(resolve) {    console.log('promise1');    resolve();}).then(function() {    console.log('promise2');});console.log('script end');//题目3async function async1(){    console.log('async1 start')    await async2()    console.log('async1 end')  }async function async2(){    console.log('async2')}console.log('script start')setTimeout(function(){    console.log('setTimeout0') },0)  setTimeout(function(){    console.log('setTimeout3') },3)  setImmediate(() => console.log('setImmediate'));process.nextTick(() => console.log('nextTick'));async1();new Promise(function(resolve){    console.log('promise1')    resolve();    console.log('promise2')}).then(function(){    console.log('promise3')})console.log('script end')

答案:
题目一:script start->a1 start->a2->promise2->script end->promise1->a1 end->promise2.then->promise3->setTimeout
题目二:script start->async1 start->async2->promise1->script end->async3->promise2->async1 end->setTimeout
在浏览器console可试验
题目三:script start->async1 start->async2->promise1->promise2
->script end->nextTick->async1 end->promise3->setTimeout->setImmediate->setTimeout3
在node环境中可试验

2. 如何实现all、如何实现链式调用的

all()的实现:函数中保护了一个数组和计数器,数组的大小为初始时all函数中传递的Promise对象数量,数组存储各个Promise执行胜利后resolve失去的后果,每胜利一个计数器+1,直到计数器累加到数组大小时即调用resolve(value),只有有一个Promise执行到失败的回调,即全副失败。
链式调用的实现:then函数返回的仍然是一个Promise,见第二版的Promise实现。

3. all中任何一个Promise出错,导致其它正确的数据也无奈应用,如何解决呢?

办法1:应用allSettled代替;办法2:改写Promise,将reject操作换成是resolve(new Error("自定义的谬误"));办法3:引入第三方库promise-transaction

4.Promise真的好用吗,它存在什么问题?

问题1:没有提供中途勾销的机制;问题2:必须要设置回调,否则外部谬误无奈在内部反映进去;问题3:应用时仍然存在大量Promise的api,逻辑不清晰。