promise实现
Promise是Javascript中的一种异步编程实现方式,js中异步编程主要是指浏览器DOM事件处理,setTimeout/setInterval
,ajax等,通过传入回调函数来实现控制反转。Promsie对象符合CommonJS编程规范,目的是为异步编程提供统一接口,它最大的优点就是避免了回调金字塔。
获取用户信息
获取用户图片
弹窗提示
一般之前使用方式为:
之前已经对
getUserInfo()
和getUserImage()
,showTip()
方法进行了定义
getUserInfo(id, function(info){ getUserImage(info.img, function(){ showTip(); })})
如果回调函数不止3个,那将会是一个非常长的回调,换成Promise实现:
//getUserInfo返回promisegetUserInfo(id) .then(getUserImage) .then(showTip) .catch(function(e){ console.log(e); });
简单的讲,Promsie的思想就是每一个异步任务返回一个promise对象,该对象有一个then方法,允许指定回调函数。这里getUserInfo
的回调函数就是getUserImage
。
getUserInfo函数要进行如下改写(这里用jQuery的Deferred()实现)
function getUserInfo(id){ var dfd = $.Deferred(); setTimeout(function(){ //获取用户信息 dfd.resolve(); }, 500); return dfd.promise;}
这样一种写法回调函数就成了链式写法,程序的流程非常清楚,可以实现多个回调。
先在ES6出来之后,许多浏览器已经支持Promise方法,promise有三种状态:pending:初始状态
fulfilled:成功操作
rejected:失败操作
开始的时候promise是pending状态,fulfill之后就会执行回调then的回调函数,如果是reject就会调用catch进行异常处理,Promise.prototype.then()
和Promise.prototype.catch()
两种方法状态都会返回一个promise对象,用于后续链式调用。
建立一个promise例子
'use strict';var promiseCount = 0;function testPromise(){ var thisPromiseCount = promiseCount++; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 开始 (异步调用开始)'); //新建一个promsie对象 var p1 = new Promise(function(resolve, reject){ log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise 开始执行 (异步调用开始)'); window.setTimeout(function(){ resolve(thisPromiseCount); }, 2000) }); //定义promise执行fulfilled执行的then()回调函数以及执行reject后的catch回调函数 p1.then( function(val) { log.insertAdjacentHTML('beforeend', val + ') Promise fulfilled (异步代码中断)'); }) .catch( function(reason) { console.log('Handle rejected promise ('+reason+') here.'); }); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 建立promise'); }if ("Promise" in window) { var btn = document.getElementById("btn"); btn.addEventListener("click",testPromise); } else { log = document.getElementById('log'); log.innerHTML = "浏览器不支持promise接口"; }}
单击按钮后,首先执行promise
3秒之后resolve调用then()方法
实现XMLHttpRequest获取数据
使用ajax异步加载图片
function imgLoad(url) { //返回promsie对象 return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'blob'; xhr.onload = function() { if (xhr.status === 200) { resolve(xhr.response); } else { reject(Error('图像加载失败; 错误原因:' + xhr.statusText)); } }; xhr.onerror = function() { reject(Error('加载错误.')); }; xhr.send(); }); } var body = document.querySelector('body'); var myImage = new Image(); var url = 'http://7qnb7a.com1.z0.glb.clouddn.com/6608717992840918931.jpg'; imgLoad(url).then(function(response) { var imageURL = window.URL.createObjectURL(response); myImage.src = imageURL; body.appendChild(myImage); }, function(Error) { console.log(Error); });
构建一个简单的promise模式框架
首先需要建立一个对象来存储promise
Promise = function(){ this.queue = []; this.value = null; this.status = 'pending';};
定义获取队列,设定状态和获取状态原型方法
Promsie.prototype.getQueue = function(){ return this.queue;};Promise.prototype.getStatus = function(){ return this.status;};Promise.prototype.setStatus = function(s, value){ if(s === 'fulfilled' || s === 'rejected'){ this.status = s; this.value = value || null; this.queue = []; var freezeObject = Object.freeze || function(){}; freezeObject(this); }else{ throw new Error({message:"doesn't support status: "+s}); }};
定义then方法,接受两个参数用于完成和拒绝任务操作。
Promise.prototype.then = function(onFulfilled, onRejected){ var handler = { 'fulfilled': onFulfilled, 'rejected': onRejected }; handler.deferred = new Deferred(); if(!this.isPending()){ utils.procedure(this.status, handler, this.value); }else{ this.queue.push(handler); } return handler.deferred.promise;};
定义三个不同状态函数
Promise.prototype.isFulfilled = function(){ return this.status === 'fulfilled';};Promise.prototype.isRejected = function(){ return this.status === 'rejected';};Promise.prototype.isPending = function(){ return this.status === 'pending';};
工具处理函数
var utils = (function(){ var makeSignaler = function(deferred, type){ return function(result){ transition(deferred, type, result); } }; var procedure = function(type, handler, result){ var func = handler[type]; var def = handler.deferred; if(func){ try{ var newResult = func(result); if(newResult && typeof newResult.then === 'function'){ newResult.then(makeSignaler(def, 'fulfilled'), makeSignaler(def, 'rejected')); }else{transition(def, type, newResult);} }catch(err){transition(def, 'rejected', err);} }else{transition(def, type, result);} }; var transition = function(deferred, type, result){ if(type === 'fulfilled'){ deferred.resolve(result); }else if(type === 'rejected'){ deferred.reject(result); }else if(type !== 'pending'){ throw new Error({'messgae':"doesn,t support type:"+type}); } }; return { 'procedure': procedure }})();
定义延迟函数
Deferred = function() { this.promise = new Promise();};Deferred.prototype.resolve = function(result) { if (!this.promise.isPending()) { return; } var queue = this.promise.getQueue(); for (var i = 0, len = queue.length; i < len; i++) { utils.procedure('fulfilled', queue[i], result); } this.promise.setStatus('fulfilled', result);};Deferred.prototype.reject = function(err) { if (!this.promise.isPending()) { return; } var queue = this.promise.getQueue(); for (var i = 0, len = queue.length; i < len; i++) { utils.procedure('rejected', queue[i], err); } this.promise.setStatus('rejected', err);}
有很多实现了promises的库供开发者可用。 像jQuery的 Deferred, 微软的 WinJS.Promise, when.js, q, 和dojo.Deferred.
关于jQuery的Deferred可以参考