promise对象是对我们现在还没有得到但是将来会得到值的占位符,他是对我们最终能够得到异步计算结果的一种保证,如果我们兑现了我们的承诺,那结果会得到一个值,当发生问题的时候,得到一个错误的结果,使用promise的最佳实践就是从服务器获取数据,我们承诺可以拿到数据,但是还有有可能发生错误

一个简单的promise:


let ninjiaPromise = new Promise(((resolve, reject) => {
        resolve('Hattori');
        //reject('an Error resolving promise');
    }));
    
    ninjiaPromise.then(ninja => {
        console.log(ninja)
    }, error => {
        console.error(error)
    });
    //Hattori

通过new创建一个promise对象,向promise构造函数中传入两个参数一个resolve,一个reject,在返回的promise对象上调用then方法,当resolve执行,代表成功,会调用then方法的第一个参数(成功的回掉函数),反之失败执行reject方法,调用then的第二个参数(失败的回掉函数)。

promise生命周期:

如上图所示,一个promise对象有三个状态,pending(等待),当调用resolve后,程序会进入Fulfilled(完成)状态,当调用的是reject,会进入Rejected(拒绝)状态。(进入拒绝状态可能是调用了reject(显示拒绝),也可能是程序抛出错误(隐式拒绝))。一旦一个promise对象进入完成状态或者拒绝状态后,他们的状态不可能切换,并且状态也是不可逆的。

链式调用catch方法:

promise对象除了可以给then传递两个状态的回掉函数,也可以支持链式调用:


let ninjiaPromise = new Promise(((resolve, reject) => {
    //resolve('Hattori');
    reject('an Error resolving promise'); //显示拒绝
}));

ninjiaPromise.then(ninja => {
    console.log(ninja);
}).catch(error => {
    console.error(error);
});
//an Error resolving promise

通过catch的链式调用让代码看起来更加的舒服。

链式调用promise对象:


getJson('data/ninjas.json')
    .then(ninjias => getJson(ninjias[0].missionsUrl))
    .then(missions => getJson(missions[0].detailsUrl))
    .then(mission => {
        //do something
    })
    .catch(error => {
        console.error('promise error!')
    });

每个then之后又返回一个promise对象,通过对promise对象的链式调用让程序能够依次运行,但是或许你能看出上述代码中有个小问题,三个promise对象链式调用,catch最后捕获的是三个promise中的错误,你很难看出是哪一个promise对象产生的错误。

promise链中的错误捕获:


getJson('data/ninjas.json')
    .then(ninjias => getJson(ninjias[0].missionsUrl), error => console.error('ninjas error:' + error))
    .then(missions => getJson(missions[0].detailsUrl), error => console.error('missions error:'+ error))
    .then(mission => {
        //do something
    })
    .catch(error => {
        console.error('mission error: '+error)
    });
 //or
 getJson('data/ninjas.json')
    .then(ninjias => getJson(ninjias[0].missionsUrl))
    .catch(error => console.error('ninjas error:' + error))
    .then(missions => getJson(missions[0].detailsUrl))
    .catch(error => console.error('missions error:'+ error))
    .then(mission => {
        //do something
    })
    .catch(error => {
        console.error('mission error: '+error)
    });

通过在每个then中传入第二个参数(触发错误的函数),可以捕获每一个promise中的错误,或者通过第二种方法链式调用,也可以捕获。

Promise.all()调用多个独立的promise:


Promise.all([getJson('data/ninjas.json'), getJson('data/mapInfo.json', getJson('data/plan.json'))])
        .then(results => {
            const ninjias = results[0];
            const mapInfo = results[1];
            const plan = results[2];
            //do something
        }).catch(error => console.error('promise error:' + error));

通过Promise.all()方法创建一个promise对象,并传入多个独立的promise任务,只有当所有的promise任务执行完毕,外面的promise对象才会被解决,如果多个promise对象中有一个promise报错,整个promise进入reject状态,然后catch方法调用,当进入Fulfilled状态,那么返回的值也是一个数组,和传入的promise的时候数组一一对应。

promise竞赛:


Promise.race([getJson('data/yoshi.json'), getJson('data/hattori.json', getJson('data/hanzo.json'))])
        .then(ninja => {
            console.log(ninja.name + ' is the first response');
            //do something
        }).catch(error => console.error('promise error:' + error));

Promise.race()方法传入一个promise任务数组并返回一个promise对象,一旦数组中的某一个promise任务第一时间完成(完成或者拒绝),就立即返回,然后被外层的promise对象处理。

promise的应用案例:

(1). 异步任务从服务端获取数据:


function getJson(url) {
    return new Promise((resolve, reject) => {
        const request = new XMLHttpRequest(); //创建一个XMLHttpRequest对象
        
        request.open("GET", url); //初始化请求
        
        request.onload = function () { //注册一个onload事件,当服务端相应被调用
            try {
                if (this.status === 200) {
                    resolve(JSON.parse(this.response));
                }
                else {
                    reject(this.status + " " + this.statusText);
                }
            }catch (e) {
                reject(e.message);
            }
        };
        
        request.onerror = function () { //当和服务端通信发生错误,被调用
          reject(this.status + ' '+ this.statusText);  
        };
        
        request.send(); //发送请求
    })
}

getJson('data/ninjas.json')
    .then(ninjas => {
        //do something
    })
    .catch(error => {
        console.error('request error: '+error)
    });

代码中有三个潜在的错误,1. 当客户端与服务端链接错误,我们通过注册request.onerror事件,当错误的时候显示拒绝。2. 服务端返回其他的状态码,显示的拒绝,3. 当返回的数据是一个无效的json,或者解析json出错,显示的拒绝。当任何错误触发的时候都可以触发回掉函数,这样就可以定位到错误bug。

生成器和promise结合的案例:


async(function* () {
    try{
        let ninjas = yield getJson('data/ninjas.json');
        let missions = yield getJson(ninjas[0].missionsUrl);
        let missionDescription = yield getJson(missions[0].detailsUrl);
        //do something
    }catch (e) {
        throw new Error(e)
    }
});

function async(generator) {
    let iterator = generator();//创建迭代器

    function handle(iteratorResult) {
        if (iteratorResult.done){return;} //如果没有可迭代,返回
        const iteratorValue = iteratorResult.value;

        if (iteratorValue instanceof Promise){ //判断当前的值是不是Promise构造函数的实例
            iteratorValue.then(res => {
                handle(iterator.next(res))
            })
                .catch(err => iterator.throw(err));
        }
    }

    try {
        handle(iterator.next());//首先调用迭代器的next方法
    }catch (e) {
        iterator.throw(e);
    }
}

这就是promise与生成器的一个结合例子,其中getJson()方法必须返回一个promise对象,否则会出错,代码中充分利用了生成器的特性挂起和恢复执行能力,和promise的异步执行链式调用,以及箭头函数等知识。

下一篇文章将介绍最新的ECMAScript中的async函数,如果文章中有错误希望大佬们留言指出,如果喜欢作者的文章可以打上支持一下😁。

注:本文章属于原创作品,可以转载,但是请你备注转载出处和作者。

--本文章已同步到微信公众平台,扫描下方二维码关注可快速阅读:--
打赏
X

感谢大佬的打赏😘