JavaScript-Promise对象基础

首先是对于Promise的一个简单的介绍,这个东西是什么,可以用来做什么。

Promise是ES6中实现语言标准的异步编程解决方案,是对于回调函数和事件的一种新的处理方案。

当然,说到这里,如果你对于Promise没有接触的话,我相信你还是会感到困惑,所以我们首先简单的给个例子。

//异步加载图片
function loadImg(url){
    return new Promise(function(resolve,reject){
        const image =new Image();                       
        image.src=url;          
        image.onload=function(){
            resolve(image);
        }           
        image.onerror=function(){               
            reject(new Error('Could not load image at'+url));
        }           
    });
}

//加载图片
loadImg("img/233.jpeg").then(function(image){
    document.getElementsByTagName("body")[0].append(image);
}).catch(function(value){
    console.log(value);
})

上面的例子还是很简单的,相信读者很容易就会猜到这是一个异步加载图片并显示的代码,当然你也会困惑,什么是resolve/reject,还有then和catch这样的方法,接下来,我会一一解释。

1.Promise

通过上面的例子,我相信读者对于Promise会有一个笼统的了解,promise的英文是“承诺”,就像我们上面的例子一样,promise封装了一个具有异步操作的函数,然后当收到执行结果时再根据状态调用对应的函数执行。

对象刚开始创建时的初始状态是pending(等待),当对象fulfill时会变为fulfilled,当对象reject(失败)时会变为reject的。而且上面所说的两个变化都是单方向而又不可逆的。

相信读者可以很快猜到,当异步的执行完成后,我们便可以通过resolve函数使状态变为fulfilled时,对象执行失败时便可以通过reject函数使状态变为rejected。而这两个函数需要作为参数传入Promise的构造函数,同时这两个参数是JavaScript引擎提供的,你只需要在函数中声明一下就可以使用了。值得一提的是,你也可以通过诸如throw new Error()的方式来改变状态,但是这需要你需要显示的声明catch函数,这在后面我们会细说。。。

你可能会感觉有一点奇怪,因为浏览器本身便会监听图片加载的状态,然后在加载完成时执行对应onload()函数,我们所做的则是在其上再进行一层封装。类似的还有对于XMLHttpRequest请求的封装,这样做其实是将回调函数剥离出来,可以更好的进行模块化复用。也正是因为这样,Promise设计的初衷便是解决“地狱回调”问题。

当然,上面的你暂时看不懂也没有关系,你现在只需要了解Promise的构造方法,学会创建一个Promise对象就可以了。
因为Promise对象封装了一个函数,所以你在创建一个新的Promise对象时需要传入一个函数,类似如下的写法:

//声明promise 函数
function promise(reslove,reject){
    var time = Math.random()*2;
    if(time <1){
        reslove(time);
    }
    else{
        reject(time);
    }
}

//创建Promise对象
var p = new Promise(promise);

注意,这种写法在你创建Promise对象时便已经开始执行,所以如果你需要模块化的处理和复用,你可以采用下面的方式:

//函数返回Promise对象
function getPromise() {
    return new Promise(function(reslove, reject) {
        var time = Math.random() * 2;
        if(time < 1) {
            reslove(time);
        } else {
            reject(time);
        }
    })
}

//创建一个Promise对象     
var p= getPromise();

2.then

Promise对象的实例方法之一便是then(),类似的,then()方法也返回一个新的Promise对象,并且可以接受两个函数参数。第一个参数会添加到fulfill时调用的数组中,第二个参数添加到reject时调用的数组中。并且,当promise状态fulfill时,会把resolve(value)中的value传个调用的函数中,例如首例中我们创建 Image对象。同理,当promise状态变为rejected时,会把reject(reason)中的reason值传调给调用的函数。

还是上面我们创建Promise对象的例子,我们再添加上then()函数。

//函数返回Promise对象
function getPromise() {
    return new Promise(function(reslove, reject) {
        var time = Math.random() * 2;
        if(time < 1) {
            reslove(time);
        } else {
            reject(time);
        }
    })
}
//创建Promise对象
var p= getPromise();
p.then(function(value){
    console.log("fulfill time = "+value);
},function(reason){
    console.log("reject time = "+reason);
})

很简单对吧!

还记得前面我说的吗?then()方法返回的是一个新的Promise对象,这意味着你可以用类似如下的方式链式调用:
new Promise(…).then(…).then(…);

//函数返回Promise对象
function getPromise() {
    return new Promise(function(reslove, reject) {
        var time = Math.random() * 2;
        if(time < 1) {
            reslove(time);
        } else {
            reject(time);
        }
    })
}
//创建Promise对象
var p= getPromise();
p.then(function(value){
    console.log("fulfill time = "+value);
    if(value<0.5){
        throw new Error("this too small");
    }
},function(reason){
    console.log("reject time = "+reason);
    if(reason>1.5){
        throw new Error("this too big");
    }
}).then(function(){
    console.log("fulfill second time");
},function(value){
    console.log(value);
})

如果你仔细思考并自己尝试,我认为你应该可以得出这样的结论,前面的Promise对象的状态是不会影响后面的Promise对象的状态的。但是,需要注意的是,如果前面的Promise对象的状态没有被正确的接受,比如,你讲上面的代码中第一个then()函数的两个函数参数全部都改为undefined的话,状态会继续传递下去,当然参数value和reason也是一样会被传下去。。。

3.catch

上面我们说过,状态可能会没有被正确接受,比如说:

new Promise(...).then(undefined,undefined);

如果上面的你都看懂了,那么接下来你会很快掌握catch,这也是Promise对象的实例方法。

then(undefined,onRejected(){
    //to do something
})

<=>

catch(onRejected(){
    //to do something
})

读到这,如果你都尝试过来,那么便可以说对Promise有了基础的了解,如果感兴趣的话,可以接着看下去,里面有Promise的静态方法。

4.resolve 和 reject

这是别的创建Promise对象的方式

Promise.resolve(5);

new Promise(function(resolve){
    resolve(5);
})

你很自然的想到,上面的方式得到的其实是fulfilled状态的对象。类似的对比,你可以创建rejected状态的对象。

Promise.reject(5);

5.all 和 race

对于all(),该静态方法接受一个Promise对象数组作为参数,并返回一个新的Promise对象。
只有当所有的对象都变为fulfilled时,新的对象才会变为fulfilled,可以想象的是,所有Promise对象的value依次添加组成一个新的数组作为新对象的resolve的value。
相对的,只要有一个对象变为rejected,新的对象就会变为rejected状态,并且将该对象的reason作为新对象reject的reason。

下面我们看两段代码

function timeout(time) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(time);
        }, time);
    });
}
console.time('promise')
Promise.all([
    timeout(10),
    timeout(60),
    timeout(100)
]).then(function (values) {
    console.log(values); [10, 60, 100]
    console.timeEnd('promise');   // 107ms 
});
function timeout(time) {
    return new Promise(function (resolve,reject) {
        setTimeout(function () {
            reject(time);
        }, time);
    });
}
console.time('promise')
Promise.all([
    timeout(10),
    timeout(60),
    timeout(100)
]).then(undefined,function (values) {
    console.log(values); [10, 60, 100]
    console.timeEnd('promise');   // 32ms 
});

我想要强调的两点是:
1.所有的Promise对象是同时开始执行的。
2.当新的Promise对象的状态已经决定了后,后面的Promise对象依旧继续执行,读者可以在reject(time)前加上console(time)来观察效果。
同时需要注意的是,如果传入的数组中有非Promise对象,在fulfill时会被一同传入resolve的value。

对比地去理解race(),你会发现也很简单。同样的是接受Promise数组作为传入参数,不同的是race的含义是竞争,最早执行完的Promise对象将决定新的Promise对象的状态。

有趣的是,如果传入的数组中也有非Promise对象,则是会直接以该值为value执行resolve。

以上便是对于Promise一个非常简单的介绍了,希望各位读后都能有所收获。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值