您的位置:首页 > Web前端 > JavaScript

学习promise编写和使用

2016-03-31 10:51 736 查看

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返回promise
getUserInfo(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例子

<button id="btn">Make a promise!</button>
<div id="log"></div>


'use strict';
var promiseCount = 0;

function testPromise(){
var thisPromiseCount = promiseCount++;
var log = document.getElementById('log');
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') 开始 (<small>异步调用开始</small>)<br/>');
//新建一个promsie对象
var p1 = new Promise(function(resolve, reject){
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise 开始执行 (<small>异步调用开始</small>)<br/>');
window.setTimeout(function(){
resolve(thisPromiseCount);
}, 2000)
});

//定义promise执行fulfilled执行的then()回调函数以及执行reject后的catch回调函数
p1.then( function(val) {
log.insertAdjacentHTML('beforeend', val +
') Promise fulfilled (<small>异步代码中断</small>)<br/>');
})
.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可以参考jQuery的deferred对象详解

Promise MDN
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript