Promise的提出是为了解决异步场景这个领域的问题. 这个章节会首先说明目前JS在处理异步时有哪些问题, 然后给出Promise处理这些问题的方案.
function wait1000(cb){
setTimeout(cb, 1000);
}
function wait1000(cb){
setInterval(cb, 1000);
}
function f1(){console.log('f1');}
wait1000(f1);
callback的调用函数(wait1000)和实现callback的函数(f1)并不在一起, callback的执行依赖于其调用方(wait1000)而不是实现方(f1), 换句话说: 我编写了一个函数,但我无法决定这个函数将被以何种方式、在何时被调用、被调用多少次。这会导致代码的状态并不可靠。
举例:
假定开发组1负责创建订单和减少库存, 开发组2负责扣款业务, 扣款业务是异步的, 需要传入回调函数cb给出扣款结果。业务流程是开发组1在创建好订单后将库存减一的动作作为回调传递给开发组2实现的扣款模块。
通常情况下一切正常,但是如果开发组2实现的代码有误,会出现以下几种可能性:
作为开发组1, 需要对后面2种场景做处理, 但组织这个逻辑比较繁琐.(变量记录+计时器). 繁琐的原因是要协调两个不同的异步过程,而callback模式下没有给出如何协调两个以上callback的普适方案.
英文中promise含义是承诺, 一个在未来(异步)兑现的结果.
承诺有两种状态: 进行中, 结束
承诺结果有两种可能性: 成功兑现, 失约
Promise的两种状态: pending, settled
settled的两种结果: resolved, rejected
Promise总会有1个结果, 结果不能被改变, 结果不能发生多次.
MDN Promise
function showContent(t){
console.log('内容:', t);
}
function showError(err){
console.log('错误信息:', err);
}
//Promise.prototype.constructor
var p = new Promise(function(resovle, reject){
fs.readFile('/tmp/l.log','utf8', function(err, data){
if(err){
reject(err);
}else{
resolve(data);
}
});
});
p.then(showContent, showError);
console.log(typeof p);
console.log(p);
console.log(p.)
Promise 对象
p1.then(()=>p2).then(...)
//Promise.prototype.then(onFulfilled, onRejected)
//Promise.prototype.catch(onRejected)
//Promise.prototype.finally(onFinally) --- ES2018
p.then(f1, f2)
p.then(f1)
p.catch(f2)
p.then(f1).catch(f3)
p.catch(f2).finally(f4)
fetch
node-fetch
fs.promises
fetch('http://127.0.0.1:8080/api/get_user_list')
.then(x=>x.json())
.then(x=>console.log(x));
组织多个异步操作
串行:
p.then().then().then()...
并行:
//等待所有执行成功
Promise.all([p1, p2, p3, ...]),then()
//等待第一个执行成功
Promise.race([p1, p2, p3, ...]).then()
//异步队列逐个执行
function p(name){
return new Promise(function(resolve, reject){
setTimeout(() => {
console.log('Time: ', new Date(), name);
resolve(Date.now());
}, 1000);
})
}
let tasks = ['A','B','C','D','E','F'];
async function quee(){
while(tasks.length>0){
await p(tasks[0]);
tasks.unshift();
}
}
quee().then(()=>{
console.log('END, Time: ', new Date());
});
变种
function inTime1000(){
return new Promise(function(resolve, reject){
setTimeout(() => {
reject(null);
}, 1000);
})
}
Promise.race([
inTime1000(),
fetch('http://127.0.0.1:8080/api/get_user_list').then(x=>x.json())
]).then(data=>{
console.log(data);
}, err=>{
console.log(err);
});
包装成常用函数: inTime(p, timeOutSecond)
function inTime(p, timeOutSecond){
function inTimeTimer(second){
return new Promise(function(resolve, reject){
setTimeout(() => {
reject(null);
}, 1000*second);
})
}
return Promise.race([inTimeTimer(1.5), p]);
}
//inTime( fetch('http://127.0.0.1:8080/api/get_user_list').then(x=>x.json()), 1.2).then();
function p(name, cb){
setTimeout(() => {
console.log('Time: ', new Date(), name);
cb();
}, 1000);
}
let tasks = ['A','B','C','D','E','F'];
function next(){
if(tasks.length>0){
let t = tasks.shift();
p(t, function(){
console.log('剩余:', tasks);
next();
});
}else{
console.log('END, Time: ', new Date());
}
}
next();
setTimeout(()=>{
tasks.push('M');
},2100);
await 同步形式的异步调用, 书写形式上更符合思维.
function p(name){
return new Promise(function(resolve, reject){
setTimeout(() => {
console.log('Time: ', new Date(), name);
resolve(Date.now());
}, 1000);
})
}
let tasks = ['A','B','C','D','E','F'];
async function quee(){
while(tasks.length>0){
await p(tasks[0]);
tasks.shift();
console.log('剩余:', tasks);
}
}
quee().then(()=>{
console.log('END, Time: ', new Date());
});
setTimeout(()=>{
tasks.push('M');
},2100);
bluebird
Q
polyfill