细说Promise

1、前言

JavaScript是单线程的,固,一次只能执行一个任务,当有一个任务耗时很长时,后面的任务就必须等待。那么,有什么办法,能够解决这类问题呢?(抛开WebWorker不谈),那就是让代码异步执行嘛。什么意思,如Ajax异步请求时,就是经过不断监听readyState的值,以肯定执行指定的回调函数。javascript

一般的异步执行有三种,回调函数、事件监听以及发布订阅,其中事件监听和发布订阅其实差很少,只是后者更加健壮一些。html

如回调函数,回调函数是应用在异步执行中最简单的编程思想。以下:java

function async(item,callback){
    console.log(item);
    setTimeout(function(){
        callback(item+1);
    },1000);    
}

在上述列子中,执行async函数时,完成打印操做,并在1秒后执行callback回调函数(但不必定是1秒,详情见”setTimeout那些事儿”)。es6

异步的主要目的就是处理非阻塞,提高性能。想象一下,若是某个操做须要通过多个async函数操做呢,以下:chrome

async(1, function(item){
    async(item, function(item){
        async(item, function(item){
            console.log('To be continued..');
        });
    });
});

是否是有点不易阅读了?编程

再好比,为了让上述代码更加健壮,咱们能够加入异常捕获。在异步的方式下,异常处理分布在不一样的回调函数中,咱们没法在调用的时候经过try…catch的方式来处理异常, 因此很难作到有效,清楚。数组

哎哟喂,那可怎么办呢?promise

噔噔噔噔,噔噔噔噔—Promise闪亮登场。app

假若用ES6的Promise优化上述代码,可得:异步

function opration(item){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(item+1);
        },1000);
    });
    console.log(item);
    return p;
}
function failed(e){
    console.log(e);
}
Promise.resolve(1).then(opration).then(opration).then(opration).catch(failed);

Promise 优化后的代码,优势显而易见,让回调函数变成了链式调用,避免了层层嵌套,使程序流程变得清晰明朗,并为一个或者多个回调函数抛出的错误经过catch方法进行统一处理。

哎呦,不错嘛,那这个ES6中的Promise究竟是何方圣神,具体使用法则是什么呢?咱们就一块儿来探究探究。

该篇博客原文地址:http://www.cnblogs.com/giggle/p/5575157.html

2、Promise概述

Promise是异步编程的一种解决方案,比传统的解决方案(回调和事件)更合理和更强大。它由社区最先提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise对象有且只有三种状态:

  一、 pending:异步操做未完成。

  二、 resolved:异步操做已完成。

  三、 rejected:异步操做失败。

又,这三种状态的变化只有两种模式,而且一旦状态改变,就不会再变:

  一、异步操做从pending到resolved;

  二、异步操做从pending到rejected;

好了,既然它是属于ES6规范,咱们再经过chrome,直接打印出Promise,看看这玩意:

 

恩,一目了然,Promise为构造函数,欧克,这样经过它,咱们就能够实例化本身的Promise对象了,并加以利用。

3、Promise入门指南

Promise既然是一个构造函数,那么咱们就先new一个看看。以下:

var p = new Promise();

并执行上述代码,chrome截图以下:

怎么报错了呢?

哦,对了,Promise构造函数,须要一个函数做为其参数哦,而且做为参数的函数中,有两个参数,第一个参数的做用为,当异步操做从pending到resolved时,供其调用;第二个参数的做用为,当异步操做从pending到rejected时,供其调用。

例如,我将匿名函数的第一个参数取名为resolve;第二个参数取名为reject。Demo以下:

var p = new Promise(function(resolve, reject){
    console.log('new一个Promise对象');
    setTimeout(function(){
        resolve('Monkey');
    },1000);
});

并执行上述代码,chrome截图以下:

特别提醒:当传入匿名函数做为构造函数Promise的参数时,咱们在new的时候,匿名函数就已经执行了,如上图。

咦,上述代码中,咱们在匿名函数中,经过setTimeout定时器,在1秒后,还调用了resolve呢,怎么没有报undefined或者错误呢?!

这就是Promise强大之处的一点。正由于这样,咱们就能够将异步操做改写成优雅的链式调用。怎么调用呢?

还记得,咱们在“Promise概述”一小节中,经过chrome打印Promise,用红线框中的区域么?其中,Promise原型中有一then方法(Promise.prototype.then),经过这个then方法,就能够了。以下:

p.then(function(value){
    console.log(value);
});

其中,then方法有两个匿名函数做为其参数,与Promise的resolve和reject参数一一对应。执行代码,结果以下:

好了,当then执行完后,若是咱们想继续在其以后看,使用then方法链式调用,有两种状况,一种是直接返回非Promise对象的结果;另外一种是返回Promise对象的结果。

一、返回非Promise对象的结果:紧跟着的then方法,resolve马上执行。并可以使用前一个then方法返回的结果。以下:

p.then(function(value){
    console.log(value);
    //返回非Promise对象,如个人对象
    return {
        name: 'Dorie',
        age: 18
    };
}).then(function(obj){
    console.log(obj.name);
});

执行上述完整代码,chrome截图以下:

 

二、返回Promise对象的结果:紧跟着的then方法,与new Promise后的then方法同样,需等待前面的异步执行完后,resolve方可被执行。以下:

p.then(function(value){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var message = value + ' V Dorie'
            resolve(message);
        },1000);
    });
    console.log(value);
    //返回一个Promise对象
    return p;
}).then(function(value){
    console.log(value);
});

执行上述完整代码,chrome截图以下:

 

那么,当建立、执行Promise方法中有异常报错,如何捕获呢?

Promise.prototype.catch原型方法,就是为其而设定的。它具备冒泡的特性,好比当建立Promise实例时,就出错了,错误消息就会经过链式调用的这条链,一直追溯到catch方法,若是找到尽头都没有,就报错,而且再找到catch以前的全部then方法都不能执行了。Demo以下(代码太长,请自行展开):

<!DOCTYPE html>
    <head>
        <title>test</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            var p = new Promise(function(resolve, reject){
                //M未定义
                console.log(M);
                setTimeout(function(){
                    resolve('Monkey');
                },1000);
            });
            p.then(function(value){
                var p = new Promise(function(resolve, reject){
                    setTimeout(function(){
                        var message = value + ' V Dorie'
                        resolve(message);
                    },1000);
                });
                console.log(value);
                //返回一个Promise对象
                return p;
            }).then(function(value){
                console.log(value);
                return 'next is catch';
            }).catch(function(e){
                console.log(e);
            }).then(function(value){
                console.log('execute,but value is ' + value);
            });
        </script>
    </body>
</html>
View Code

执行上述代码,chrome截图以下:

 

好了,到这里,咱们已经了解了最经常使用的Promise.prototype.then和Promise.prototype.catch这两个原型方法。另外,像Promise构造函数还有属于自身的方法,如all、rece、resolve、reject等,详情请点击这里(here)。

经过一路上对Promise的讲述,咱们也有了必定的认识,其实Promise并无想象中的那么难以理解嘛。懂得Promise概念后,其实咱们本身也能够实现一个简易版的Promise。下面就一同尝试实现一个呗。

4、模拟Promise

假设:有三个异步操做方法f1,f2,f3,且f2依赖于f1,f3依赖于f2。若是,咱们采用ES6中Promise链式调用的思想,咱们能够将程序编写成这样:

f1().then(f2).then(f3);

那么,经过上面这一系列链式调用,怎样才能达到与ES6中Promise类似的功能呢?

初步想法:首先将上述链式调用的f二、f3保存到f1中,当f1中的异步执行完后,再调用执行f2,并将f1中的f3保存到f2中,最后,等f2中的异步执行完毕后,调用执行f3。详细构思图,以下:

 

从上图可知,因为f一、f2 、f3是可变得,因此存储数组队列thens,可放入,咱们即将建立的模拟Promise构造函数中。具体实现代码以下:

//模拟Promise
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    }
};

而且,须要一个Promise.prototype.resolve原型方法,来实现:当f1异步执行完后,执行紧接着f1后then中的f2方法,并将后续then中方法,嫁接到f2中,如f3。具体实现代码以下:

//模拟Promise,增长resolve原型方法
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function(){
        var t = this.thens.shift(), 
            p;
        if(t){
            p = t.apply(null,arguments);
            if(p instanceof Promise){
                p.thens = this.thens;
            }
        }
    }
};

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {
       
        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}
f1().then(f2).then(f3);
测试代码

仔细品味,上述实现的Promise.prototype.resolve方法还不够完美,由于它只可以知足于f一、f二、f3等方法都是使用模拟的Promise异步执行的状况。而,当其中有不是返回的Promise对象的呢,而是返回一个数字,亦或是什么也不返回,该怎么办?因此,针对以上提出的种种可能,再次改进resolve。改善代码以下:

//模拟Promise,改善resolve原型方法
var Promise = function () {
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function () {
        var t,p;
        t = this.thens.shift();
        t && (p = t.apply(null, arguments));
        while(t && !(p instanceof Promise)){
            t = this.thens.shift();
            t && (p = t.call(null, p));    
        }
        if(this.thens.length){
            p.thens = this.thens;
        };
    }
}

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {
       
        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}

function f4() {
    console.log(4);
    return 11;
}

function f5(x) {
    console.log(x+1);
}

function f6() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(6);
        promise.resolve();
    }, 1500)

    return promise;
}

function f7() {
    console.log(7);
}

var that = f1().then(f2).then(f3).then(f4).then(f5).then(f6).then(f7);
测试代码

好了,初步模拟的Promise就OK啦。

吼吼,对于Promise,咱们这一路走来,发现原来也不过如此呢。

5、拓展阅读

[1]、再谈Event Loop

[2]、MDN

[3]、大白话讲解Promise(一)

[4]、当耐特

[5]、Promise & Deferred objects in JavaScript Pt.1

相关文章
相关标签/搜索