← 返回首页
Javascript基础教程(三十五)
发表时间:2020-04-01 17:45:56
讲解Javascript的promise

1.什么是promise

JavaScript中的 promise 与现实生活中的承诺非常相似。首先让我们看看现实生活中的承诺。

promise 词典中的定义:保证某人会做某事或某件事会发生。

那么当有人向你承诺时,会发生什么呢?

由此我们可以看出Promise是ES6中新增的一种异步编程的方式,用于解决回调的方式的各种问题,提供了更多的可能性。

2.promise的状态

Promise对象一共有3中状态,pending,fullfilled(又称为resolved)和rejected: - pending——任务仍在进行中。 - resolved——任务已完成。等同于兑现承诺 - reject——任务出错。无法兑现承诺

Promise对象初始时处于pending状态,其生命周期内只可能发生以下一种状态转换: - 任务完成,状态由pending转换为resolved。 - 任务出错返回,状态由pending转换为rejected。

Promise对象的状态转换一旦发生,就不可再次更改。这或许就是Promise之“承诺”的含义吧。

使用 Promise 的优点:

使用 Promise 的缺点: - 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。 - 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。 - 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

3.promise基本用法

1)创建Promise

let p = new Promise(executor(resolve, reject));

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

Promise 新建后立即执行,then 方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,catch 同理。

例如:

<script>
    let flag = true;
    let fn = function () {
        return new Promise((resolve, reject) => {
            if (flag) {
                resolve('成功');
            } else {
                reject('失败');
            }
        });
    }

    promise = fn();

    promise.then((resp) => {
        console.log('in the then function....');
    })
    console.log("输出promise对象=>");
    console.log(promise);
</script>

运行结果:
输出promise对象=>
Promise {<fulfilled>: "成功"}
in the then function....

说明先输出的promise对象再执行了then方法。

比如,男朋友以前承诺情人节给女朋友,送礼物。当然这仅仅是个承诺,能否兑现谁也不清楚。可以用promise来描述这个故事。

<script>
    let keepWord;
    keepWord = true;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友存款不够,无法兑现承诺!");
        }
    });
    console.log(promise);
</script>
运行结果:
Promise {<resolved>: "男朋友信守承诺给女朋友送iphone11"}

2)then Promise对象创建完成之后,我们需要调用then(succ_handler, fail_handler)方法指定成功和/或失败的回调处理。 例如:

<script>
    let keepWord;
    keepWord = false;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友存款不够,无法兑现承诺!");
        }
    });
    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友一口!");
    },function(reject){
        console.log(JSON.stringify(reject)+",罚跪搓衣板!");
    })
    console.log(promise);
</script>

运行结果:
Promise {<rejected>: "男朋友存款不够,无法兑现承诺!"}
"男朋友存款不够,无妨兑现承诺!",罚跪搓衣板!

这就是最基本的使用Promise编写异步处理的方式了。但是,有几点需要注意: (1)then方法可以只传入成功或失败回调。 (2)executor函数是立即执行的,而成功或失败的回调函数会到当前EventLoop的最后再执行。下面的代码可以验证这一点: (3)then方法返回的是一个新的Promise对象,所以可以链式调用。 例如:

<script>
    let keepWord;
    keepWord = true;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友存款不够,无法兑现承诺!");
        }
    });
    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友一口!");
        return resolve;
    },function(reject){
        console.log(JSON.stringify(reject)+",罚跪搓衣板!");
    }).then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友第二口!");
    })
    console.log(promise);
</script>

运行结果:
Promise {<resolved>: "男朋友信守承诺给女朋友送iphone11"}
"男朋友信守承诺给女朋友送iphone11",亲男朋友一口!
"男朋友信守承诺给女朋友送iphone11",亲男朋友第二口!

(4)Promise对象的then方法可以被调用多次,而且可以被重复调用。 例如:

<script>
    let keepWord;
    keepWord = true;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友存款不够,无法兑现承诺!");
        }
    });
    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友一口!");
    },function(reject){
        console.log(JSON.stringify(reject)+",罚跪搓衣板!");
    });

    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友第二口!");
    },function(reject){
        console.log(JSON.stringify(reject)+",罚跪搓衣板!");
    });

    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友第三口!");
    },function(reject){
        console.log(JSON.stringify(reject)+",罚跪搓衣板!");
    });
    console.log(promise);
</script>

运行结果:
Promise {<resolved>: "男朋友信守承诺给女朋友送iphone11"}
"男朋友信守承诺给女朋友送iphone11",亲男朋友一口!
"男朋友信守承诺给女朋友送iphone11",亲男朋友第二口!
"男朋友信守承诺给女朋友送iphone11",亲男朋友第三口!

3)catch 由前面的介绍,我们知道,可以由then方法指定错误处理。但是ES6提供了一个更好用的方法catch。直观上理解可以认为catch(handler)等同于then(null, handler)。

<script>
    let keepWord;
    keepWord = false;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友失踪...");
        }
    });
    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友一口!");
        return resolve;
    }).catch(function(err){
        console.log("出现异常:"+JSON.stringify(err));
    })
    console.log(promise);
</script>

运行结果:
Promise {<rejected>: "男朋友失踪..."}
出现异常:"男朋友失踪..."

4)finally finally表示在执行完then 或 catch 指定的回调函数以后,都会执行finally方法指定的回调函数。 例如:

<script>
    let keepWord;
    keepWord = false;
    promise = new Promise(function(resolve, reject) {
        if (keepWord) {
            resolve("男朋友信守承诺给女朋友送iphone11");
        } else {
            reject("男朋友失踪...");
            return new Error("男朋友失踪...");
        }
    });
    promise.then(function(resolve){
        console.log(JSON.stringify(resolve)+",亲男朋友一口!");
        return resolve;
    }).catch(function(err){
        console.log("出现异常:"+JSON.stringify(err));
    }).finally(function(){
        console.log('无论是否有男朋友,都要对自己好一些...');
    })
    console.log(promise);
</script>

运行结果:
Promise {<rejected>: "男朋友失踪..."}
出现异常:"男朋友失踪..."
无论是否有男朋友,都要对自己好一些...

4.实际案例

下面给出一个promise结合AJAX的一个案例。

<script>
let getJSON = function (url) {
    return new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) {
                return;
            }

            if (xhr.status === 200) {
                resolve(xhr.response);
            } else {
                reject(new Error(xhr.statusText));
            }
        }
        xhr.send();
    });
}

getJSON("http://api.icndb.com/jokes/random")
 .then(function (responseText) {
    return JSON.parse(responseText);
})
 .then(function (obj) {
    console.log(obj.value.joke);
})
 .catch(function (err) {
    console.log(err.message);
});
</script>

getJSON函数接受一个url地址,请求json数据。但是请求到的数据是文本格式,所以在第一个then方法的回调中使用JSON.parse将其转为对象,第二个then方法回调再进行具体处理。

5.深入理解then then方法遵循以下原则: - then方法提供一个供自定义的回调函数,若传入非函数,则会忽略当前then方法。 - 回调函数中会把上一个then中返回的值当做参数值供当前then方法调用。 - then方法执行完毕后需要返回一个新的值给下一个then调用(没有返回值默认使用undefined)。 - 每个then只可能使用前一个then的返回值。

以下实例:

<script>
    let func = function() {
        return new Promise((resolve, reject) => {
            resolve('成功');
        });
    };
    let foo = function() {
        console.log('一定要执行foo方法...');
        return 'foo';
    }
    func().then(function () {
        return foo();
    }).then(resp => {
        console.warn(resp);
        console.warn('1 =========>');
    });

    func().then(function () {
        foo();
    }).then(resp => {
        console.warn(resp);
        console.warn('2 =========>');
    });

    func().then(foo()).then(resp => {
        console.warn(resp);
        console.warn('3 =========>');
    });

    func().then(foo).then(resp => {
        console.warn(resp);
        console.warn('4 =========>');
    });
</script>

运行结果:

一定要执行foo方法...
一定要执行foo方法...
一定要执行foo方法...
一定要执行foo方法...
foo
1 =========>
undefined
2 =========>
成功
3 =========>
foo
4 =========>

小结 Promise是ES6新增的一种异步编程的解决方案,使用它可以编写更优雅,更易读,更易维护的程序。Promise已经应用在各个角落了,个人认为掌握它是一个合格的Javascript开发者的基本功。