一直以来,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优点之一,即错误处理机制进行介绍,以及和传统的回调方式的对比。