一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心,常见的callback队列代码如下:

loadImg('a.jpg', function() {
    loadImg('b.jpg', function() {
        loadImg('c.jpg', function() {
            console.log('all done!');
        });
    });
});

这也就是我们常说的js中的回调金字塔,一旦异步的任务开始多起来,维护大量的callback将是一场灾难。

promise在这种情况下应运而生,将复杂的异步处理轻松地进行模式化,可以将我们从回调金字塔里面解放出来,改以链式的平行处理方式。

接下来,让我们在实践中来学习JavaScript的Promise吧。

1.Promise简介

目前,原生Promise主要定义了以下几类api:

1.Constructor(构造方法)

Promise类似于 XMLHttpRequest ,从构造函数 Promise 来创建一个新建新 promise 对象。 
要想创建一个promise对象、可以使用 new 来调用 Promise 的构造器来进行实例化。

var promise = new Promise(function(resolve, reject) { 
// 异步处理
// 处理结束后、调用resolve 或 reject
});

2.Instance Method(实例方法)

对于已经实例化过的promise对象可以调用promise.then()方法,传递resolve和reject方法作为回调。 
这是promise最为常用的方法。

promise.then(onFulfilled, onRejected)

promise简化了对error的处理,上面的代码我们也可以这样写:

promise.then(onFulfilled).catch(onRejected)

这时,当promise操作链任意时候出现异常时都会被catch,而这在callback回调里面是难以操作的事情。

3.Static Method(静态方法)

Promise作为全局对象提供了一些静态方法,实际上是对then方法的简写。包括Promise.all() ,Promise.resolve(),Promise.reject()等。

我们接下来尝试对前言里面的实例做一下promise改造:

function loadImg(imgSrc) {
    return new Promise(function (resolve, reject) {
        var Img = new Image();
        Img.addEventListener("load",function(){
            resolve(imgSrc);
        });

        Img.addEventListener("error",function(){
            reject(new Error("failed"));
        });

        Img.src = imgSrc;
    }); 
}

function taskA() { 
    loadImg('a.jpg')
}
function taskB() {
    loadImg("b.jpg"); 
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask(img) {
    console.log(img); 
}


var promise = Promise.resolve();
promise.then(taskA).then(taskB).then(finalTask).catch(onRejected);

2.Promise的状态周期

promise实例有3个状态:

resolve :成功,此时会调用then方法的第一个回调onFullfilled; 
reject:失败,此时会调用then方法的第二个回调onRejected; 
Pending:初始化状态 
promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再发生任何变化,并且resolve和reject之间不能相互转换。

3.编写Promise代码

创建promise对象的流程如下:

new Promise(fn) 返回一个promise对象 
在 fn 中指定异步等处理 
处理结果正常的话,调用 resolve(处理结果值) 
处理结果错误的话,调用 reject(Error对象) 
我们尝试下用promise来改写jquery ajax方法:

function ajax(URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest(); 
            req.open('GET', URL, true);
             req.onload = function () {
                if (req.status === 200) { 
                    resolve(req.responseText);
                } else {
                reject(new Error(req.statusText));
                } 
            };
            req.onerror = function () {
             reject(new Error(req.statusText));
            };
            req.send(); 
        });
    }


    var URL = "./demo1.html"; 
    ajax(URL)
        .then(function onFulfilled(value){
            console.log(value); 
        }).catch(function onRejected(error){
            console.error(error); 
        });

当请求status为200时执行resolve,其他情况下执行失败,回调方法里面通过then里onFulfilled获取成功的回调,参数为reslove返回的值,catch获取失败时的回调。

总结

本章节我们介绍了promise的api,状态周期,并通过对比promise和传统js异步回调的对比来介绍promise的优势,最后通过用promise改写jquery的ajax方法实现,接下来的章节将会对Promise优点之一,即错误处理机制进行介绍,以及和传统的回调方式的对比。