Javascript 采用回调函数(callback)来处理异步编程,但多层回调嵌套会形成回调金字塔(Pyramid of Doom)。
于是便有了CommonJS的 Promise/A 规范用于解决回调金字塔问题。
new Promise() 构造函数同步执行,.then() 异步执行。
JavaScript 中一个常见的嵌套回调如下:
// 回调金字塔
asyncOperation(function(data){
//处理 data
anotherAsync(function(data2){
//处理 data2
yetAnotherAsync(function(){
//完成
});
});
});
引入 Promise 之后:
promiseSomething()
.then(function(data){
//处理 data
return anotherAsync();
})
.then(function(data2){
// 处理 data2
return yetAnotherAsync();
})
.then(function(){
// 完成
});
Promise 将嵌套的 callback ,改造成一系列的 .then() 的连缀调用。
在 ES6 中 Promise 提供了以下 API:
(1)构造函数
function Promise(resolve){}
(2)原型方法
Promise.prototype.then = function(){}
Promise.prototype.catch = function(){}
(3)静态方法
Promise.resolve = function(){}
Promise.reject = function(){}
Promise.all = function(){}
Promise.race = function(){}
promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)。
基于这个原理,我们自己简单实现以下:
function Promise(executor){
function resolve(value){}
function reject(value){}
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
var promise = new Promise((resolve,reject)=>{
console.log('start')
})
新的写法:
const PENDING = 'pending';
const RESOLVE = 'resolve';
const REJECT = 'reject';
class Promise{
constructor(excutor){
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectCallbacks = [];
excutor(resolve,reject);
}
let resolve = (value)=>{
if(this.status === PENDING){
this.status = RESOLVE;
this.value = value;
this.onResolvedCallbacks.forEach(fn=>{
fn()
});
}
}
let reject = ()=>{
if(this.status === PENDING){
this.status = REJECT;
this.reason = reason;
this.onRejectCallbacks.forEach(fn=>{
fn()
});
}
}
then(onFulfilled,onReject){
if(this.status === RESOLVE){
onFulfilled(this.value);
}else if(this.status === REJECT){
onReject(this.reason);
}else if(this.status === PENDING){
this.onResolvedCallbacks.push(()=>{// Aop
//todo
onFulfilled(this.value)
})
this.onRejectCallbacks.push(()=>{
onReject(this.reason)
})
}
}
}
其他补充:
<div id="test-promise-log"></div>
<script>
//清除log
var logging = document.getElementById('test-promise-log');
while(logging.children.length > 1){
logging.removeChild(logging.children[logging.children.length - 1]);
}
//输出log
function logs(s){
var p = document.createElement('p');
p.innerHTML = s;
logging.appendChild(p);
}
//
new Promise(function(resolve,reject){
logs('start new Promise');
var timeOut = Math.random()*2;
console.log('set timeout to:'+ timeOut + ' seconds.');
setTimeout(function(){
if (timeOut < 1){
console.log('call resolve() ...');
resolve('200 OK');
} else{
console.log('call reject() ...');
reject('timeout in '+ timeOut + 'seconds');
}
},timeOut*1000)
}).then(function(r){
console.log('Done:' + r);
}).catch(function(reason){
console.log('Failed: ' + reason)
});
//
function multiply(input){
return new Promise(function(access,rejecrt){
console.log('calculating ' + input + 'x' + input + '...');
setTimeout(access,500,input*input);
});
}
//
function add(input){
return new Promise(function(resolce,reject){
console.log('calculating ' + input + '+' + input + '...');
setTimeout(resolce,500,input + input);
});
}
var p = new Promise(function(resolve,reject){
console.log('start new Promise ...');
resolve(123);
}).then(multiply)
.then(add)
.then(multiply)
.then(add)
.then(function(result){
console.log('Got Value: ' + result);
})
// AJAX异步执行函数转换为Promise对象
function ajax(method,url,data){
var request = new XMLHttpRequest();
return new Promise(function(resolve,reject){
request.onreadystatechange = function(){
if (request.readyState === 4){
if (request.status === 200) {
resolve(request.responseText);
}else{
reject(request.status);
}
}
};
request.open(method,url);
request.send(data);
});
}
var p = ajax('GET','url');
p.then(function(text){
console.log(text);
}).catch(function(status){
console.log('ERROR: ' + status);
})
</script>