pormise(一) 用法

489 阅读5分钟

什么是 promise

在控制台输入 console.dir(Promise)

Promise

Promise是一个构造函数,自己身上有all、reject、resolve方法,原型上有then、catch等方法。

new 一个promise出来

let p = new Promise(function(resolve, reject){
    //做一些异步操作
    setTimeout(function(){
        console.log('执行完成');
        resolve('resolve 的数据');
    }, 2000);
});
  • Promise 的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject。
  • resolve 表示异步操作执行成功后的回调函数。
  • reject 表示异步操作执行失败后的回调函数。
  • 其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve 是将 Promise 的状态置为fullfiled,reject 是将 Promise 的状态置为rejected。

上述代码的执行结果是,在2秒钟之后输出“执行完成”。 但是上述代码只是new了一个对象,并没有调用它,传进去的函数就已经执行了。所以用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数:

function runPromise(){
    let p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成');
            resolve('resolve的数据');
        }, 2000);
    });
    return p;
}
runPromise();

** 在包装好的函数最后会 return 出Promise对象,也就是说,执行这个函数最后得到了一个Promise对象 **

runPromise().then(function(data){
    console.log(data);
});

执行结果:会在2秒后输出“执行完成”,紧接着输出“resolve的数据”。

//一般回调函数的写法也能做到上述代码的功能
function runCallBack(callback){
    setTimeout(function(){
        console.log('执行完成');
        callback('callback 数据');
    }, 2000);
}
runCallBack(function(data){
    console.log(data);
});

//如果我需要在函数里再嵌套回调函数
function runCallBack(callback){
    setTimeout(function(){
        console.log('执行完成');
        callback('callback 第一层数据');
    }, 2000);
}
runCallBack(function(data,function(data){
console.log(data);
}){
    console.log(data);
	callback('callback 第二层数据');
});

Promise 的优点:链式操作

即将层级操作转换为了链式操作。

但是,链式操作并不是Promise的精髓,其精髓在于可以用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

runPromise1()
.then(function(data){
    console.log(data);
    return runPromise2();
})
.then(function(data){
    console.log(data);
    return runPromise3();
})
.then(function(data){
    console.log(data);
});

//三个异步操作函数
function runPromise1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('resolve数据1');
        }, 1000);
    });
    return p;
}
function runPromise2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('resolve数据2');
        }, 2000);
    });
    return p;
}
function runPromise3(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('resolve数据3');
        }, 2000);
    });
    return p;
}
/*
输出结果:
异步任务1执行完成
resolve数据1
异步任务2执行完成
resolve数据2
异步任务3执行完成
resolve数据3
*/

reject 用法

reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。

//定义一个 getNumber 函数
function getNumber(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            const number = Math.ceil(Math.random()*10); //生成1-10的随机数
            if(number<=5){
                resolve(number);
            }else{
                reject('数字太大了');
            }
        }, 2000);
    });
    return p;
}
//执行
getNumber().then(
    function(data){
        console.log(data);
    },
    function(reason){
        console.log(reason);
    }
);

catch 的用法

它和then的第二个参数一样,用来指定reject的回调。

getNumber()
.then(function(data){
    console.log(data);
})
.catch(function(reason){
    console.log(reason);
});

好处:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
	console.log('rejected');
    console.log(reason);
});
/*
resolved
4
rejected
ReferenceError:someData is not defined(...)
*/

** 也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中 **

all 的用法

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。 仍旧使用上面定义好的 runPromise1、runPromise2、runPromise3 这三个函数,看下面的例子:

Promise
.all([runPromise1(), runPromise2(), runPromise3()])
.then(function(results){
    console.log(results);
});
/*
异步任务1执行完成
异步任务2执行完成
异步任务3执行完成
["resolve数据1","resolve数据2","resolve数据3"]
*/
/*
all方法的效果实际上是「谁跑的慢,以谁为准执行回调],即以all里面执行时间最久的函数为标准,要等最后一个函数执行完成之后才会执行then里面的。
*/
  • 有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据。
  • 有一个场景是很适合用这个的,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。

race的用法

Promise
.race([runPromise1(), runPromise2(), runPromise3()])
.then(function(results){
    console.log(results);
});
/*
异步任务1执行完成
resolve数据1
异步任务2执行完成
异步任务3执行完成
*/
/*
与 all 用法一样,但是作用相反,它是以race里面执行时间最短的函数为标准,只需要执行时间最短的那个函数执行完毕,就会执行then里面的操作,所以在执行了 runPromise1()里面的操作之后立即就执行了then里面的。但是 runPromise2(), runPromise3()并不会中断执行,仍然会在1s后输出执行完成。
*/

使用场景

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});