一、含义
The Promise object is used for asynchronous computations. Promis对象用作异步计算
A Promise represents a value which may be available now,or in the future, or never. 一个Promise.一个Promise表示一个现在、将来或用不可能可用的值。
按照用途来解释
主要用于异步计算
可以将异步操作队列化,按照期望的顺序执行,返回符合与其的结果
可以在对象之间传递和操作Promise,帮助我们处理队列
二、为什么有Promise
Javascript包含大量异步操作 有了Node.js之后,对异步的依赖进一步加剧了 异步操作的常见语法 事件帧听与响应:
document.getElementById('demo').addEventListener('click',start,false);
function start(){
}
//jQuery使用方法
$('#demo').on('click',start);
回调:
//比较常见的是AJAX
$.ajax('http://baidu.com',{
success:function(res){
//这里就是回调函数
}
})
//或者在页面加载完毕后回调
$(function(){
//这里也是回调函数
})
三、异步回调的问题
嵌套层次很深,难以维护(回调地域)
无法正常使用return和throw
无法正常检索堆栈信息
多个回调之间难以建立联系
四、Promise简介
new Promise(
// 执行器 executor
function (resolve,reject){
//一段很长的异步操作
resolve();//数据处理完成
reject();//数据处理出错
}
).then(
function A(){
//成功,下一步
},
function B(){
//失败,做相应处理
}
)
Promise 是一个代理对象,它和原先要进行的操作并无关系。
它通过引入一个回调,避免更多的回调。
三个状态
Pending [待定] 初始状态
Fulfilled [实现] 操作成功
Rejected [被否决] 操作失败
特点
Promise状态发生改变,就会触发 .then()里的响应函数处理后续步骤。
Promise状态一经改变,不会再变。
流程图
new 一个Promise 传入执行器,异步操作。后续处理调用第一个then(),之后调用第二个then(),最终生成一个队列,每个then()都会返回一个新的Promise实例。当执行器executor执行器执行完毕,开始判断状态,调用then的函数,一个then执行完毕,又会执行下一个then,直至所有then()执行完。
五、最简单的实例
console.log('here we go');
new Promise(
resolve => {
setTimeout(()=>{
resolve('hellooo ');
},2000)
}
).then(
value => {
console.log(value + 'world')
}
)
两步执行的范例
console.log('here we go');
new Promise(
resolve => {
setTimeout(()=>{
resolve('hello');
},2000);
}
).then(
value => {
console.log(value);
return new Promise(
resolve => {
setTimeout(()=>{
resolve('world');
},2000)
}
)
}
).then(
value =>{
console.log(value +' ohhhhhhhhh')
}
)
//执行结果
//here we go
//hello
//world ohhhhhhhhh
六、对已完成的Promise执行then
假如一个Promise已经完成了,再.then()会怎样?
console.log('start');
let promise = new Promise(
resolve => {
setTimeout(()=>{
console.log('the promise fulfilled');
resolve('hello');
},1000);
}
)
setTimeout(()=>{
promise.then(
value => {
console.log(value + ' world');
}
)
},3000)
//结果
//start
//the promise fulfilled
//helloworld
在任何一个地方生成Promis队列之后,我们可以把它作为一个变量传递到其他地方。在后续可以追加任意多的then(),不管之前的promise是完成了还是没有完成,队列都会按照固定的顺序去执行。如果已完成,后面的then()也会得到之前promise的值。
七、then里不返回Promise
加入.then()的函数里面不返回新的Promise,会怎样?
console.log('here we go');
new Promise(
resolve => {
setTimeout(()=>{
resolve('hello');
},1000)
}
).then(
value => {
console.log(value);
//增加立即执行函数
(function(){
return new Promise(
resolve => {
setTimeout(()=>{
console.log('nonononono')
resolve('nonono')
},2000)
}
)
})()
return false;
}
).then(
value => {
console.log(value)
}
)
//结果
//here we go
//hello
//false
//nonononono
八、引出.then()
.then()接受两个函数作为参数,分别代表fulfilled和rejected
.then()返回一个新的Promise实例,所以它可以链式调用
当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行
状态响应函数可以返回新的Promise,或其他值
如果返回新的Promise,那么下一级.then()会在新Promise状态改变之后执行
如果返回其他任何值,则会立即执行下一级.then()
.then()嵌套
因为 .then() 返回的还是Promise实例
会等里面的 .then()执行完,再执行外面的
对于我们来说,此时最好将其展开,会更好读
console.log('start');
new Promise(resolve => {
console.log('step 1');
setTimeout(()=>{
resolve(100);
},1000)
})
.then(
value => {
return new Promise(
resolve=>{
console.log('step1-1')
setTimeout(()=>{
resolve(110);
},1000)
}
).then(
value => {
console.log('step 1-2');
return value;
}
).then(
value => {
console.log('step 1-3');
return value;
}
)
}
).then(
value => {
console.log(value);
console.log('step 2');
}
)
//结果
//start
//step 1
//1-1
//step 1-2
//step 1-3
//110
//step 2
//将嵌套的.then()提取出来
console.log('start');
new Promise(resolve => {
console.log('step 1');
setTimeout(()=>{
resolve(100);
},1000)
})
.then(
value => {
return new Promise(
resolve=>{
console.log('step1-1')
setTimeout(()=>{
resolve(110);
},1000)
}
)
}
)
.then(
value => {
console.log('step 1-2');
return value;
}
).then(
value => {
console.log('step 1-3');
return value;
}
)
.then(
value => {
console.log(value);
console.log('step 2');
}
)
//结果相同
//start
//step 1
//1-1
//step 1-2
//step 1-3
//110
//step 2
九、四个问题
下面的四种Promise的区别是什么
问题一
问题二
问题三
doSomethingElse()是以执行的方式传入进去的,实际上传的是一个Promise实例。这种情况下,doSomething和doSomethingElse(undefined)几乎同时执行,它们是在同一个栈当中去执行的。因为doSomethingElse返回的是一个Promise实例,不是一个函数,在Promise规范定义当中,第一个.then()会被忽略掉。所以finalHandler最后执行时帧听的是doSomething的完成时间。doSomethingElse执行时间无关紧要,因为不处在队列当中。
问题四
问题来源
fexteam.gz01.bdysite.com/blog/2015/0…
十、错误处理
Promise会自动捕获内部异常,并交给rejected响应函数处理
第一种方式 throw new Error('错误信息').catch(message=>{}) (推荐)
console.log('start');
new Promise(
resolve => {
setTimeout(()=>{
throw new Error('bye')
},2000)
}
).then(
value => {
console.log('hahah'+value)
}
).catch(
error => {
console.log('Error'+error.message)
}
)
//会报错,直接跳过.then进入.catch
第二种方式 reject('错误信息').then(null,message=>{})
console.log('start');
new Promise(
(resolve,reject) => {
setTimeout(()=>{
reject('bye')
//以下方法会跟第一种类似
//throw new Error('bye')
},2000)
}
).then(
value => {
console.log('ok');
},
oh => {
console.log('error'+oh)
}
)
十一、错误和then连用
console.log('start');
new Promise(
resolve => {
setTimeout(()=>{
resolve();
},1000)
}
).then(
()=>{
console.log('start2');
throw new Error('test Error');
}
).catch(
error => {
console.log('I catch ',error)
//下面这一行的注释将引发不同的走向
//throw new Error('another error')
}
).then(
()=>{
console.log('arrive here');
}
).then(
()=>{
console.log('... and here');
}
).catch(
err => {
console.log('error',err)
}
)
结果
start
start2
I catch Error: test Error
at Promise.then (/home/leondon/Desktop/nodeJsDemo/express-middleware-demo/src/Promise.js:12:15)
arrive here
... and here
建议在所有队列最后都加上.catch(),以避免漏掉错误处理造成意向不到的问题
十二、Promise.all()
批量执行
Promise.all([p1,p2,p3,......])用于将多个Promise实例,包装成一个新的Promise实例。
返回的实例就是普通Promise
它接收一个数组作为参数
数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
当所有子Promise都完成,该Promise完成,返回值是全部值的数组
有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果
console.log('here we go');
Promise.all([1,2,3]).then(
all => {
console.log('1:',all);
return Promise.all([function(){
console.log('ooxx');
},'xxoo',false]);
}
).then(
all => {
console.log('2:',all);
let p1 = new Promise(
resolve => {
setTimeout(()=>{
resolve('I\'m P1')
},1500)
}
)
let p2 = new Promise(
resolve => {
setTimeout(()=>{
resolve('I\'m P2')
},1450)
}
)
return Promise.all([p1,p2]);
}
).then(
all => {
console.log('3:',all);
let p1 = new Promise(
(resolve,reject) => {
setTimeout(()=>{
resolve('I\'m P1');
},1500)
}
)
//时间最早,所以P2被触发
let p2 = new Promise(
(resolve,reject) => {
setTimeout(()=>{
reject('I\'m P2')
},1000)
}
)
let p3 = new Promise(
(resolve,reject) => {
setTimeout(()=>{
reject('I\'m P3')
},3000)
}
)
return Promise.all([p1,p2,p3])
}
).then(
all => {
console.log('all',all);
}
).catch(
error => {
console.log('catch',error)
}
)
结果
here we go
1: [ 1, 2, 3 ]
2: [ [Function], 'xxoo', false ]
3: [ 'I\'m P1', 'I\'m P2' ]
catch I'm P2
Promise.all()与.map连用
Promise.all()最常见就是和.map()连用
十三、实现队列
有时候我们不希望所有动作一起发生,而是按照一定顺序,逐个进行
使用forEach()函数
function queue(things){
let promise = Promise.resolve();
things.forEach(
thing =>{
promise = promise.then(()=>{
return new Promise(resolve => {
doThing(thing,()=>{
resolve();
})
})
})
}
);
return promise;
}
使用.reduce()
reduce()函数作用是遍历数组,从数组的一端到另一端
function queue(things){
return things.reduce(
(resolve,reject)=>{
return Promise.then(()=>{
return new Promise(resolve=>{
doThing(thing,()=>{
resolve();
})
})
})
},Promise.resolve()
);
}
十四、Promise.resolve()
返回一个fulfilled的Promise实例,或原始Promise实例
- 参数w为空,返回一个状态为fulfilled的Promise实例
- 参数是一个跟Promise无关的值,同上,不过fulfilled响应函数会得到这个参数
- 参数为Promise实例,则返回该实例,不做任何修改
- 参数是thenable,立刻执行它的.then()
console.log('start');
Promise.resolve().then(
(value) => {
console.log('Step1',value);
return Promise.resolve('Hello');
}
).then(
value => {
console.log(value,'world');
return Promise.resolve(new Promise(resolve => {
setTimeout(()=>{
resolve('Good')
},2000)
}))
}
).then(
value => {
console.log(value,'evening');
return Promise.resolve({
then(){
console.log(', everyone')
}
})
}
)
结果
start
Step1 undefined
helllllllll world
Good evening
, everyone
十五、Promise.reject()
返回一个rejected的Promise实例。
Promise.reject()不认thenable
let promise = Promise.reject('something wrong');
promise.then(
()=>{
console.log('its ok');
}
).catch(
()=>{
console.log('no its not ok');
return Promise.reject({
then(){
console.log('it will be ok');
},
catch(){
console.log('not yet')
}
})
}
)
结果
no its not ok
(node:22203) UnhandledPromiseRejectionWarning: #<Object>
(node:22203) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
(node:22203) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
十六、Promise.reject()
类似Promise.all(),区别在于它有一个完成就算完成
let p1 = new Promise(resolve => {
setTimeout(()=>{
resolve('i am p1')
},10000)
})
let p2 = new Promise (resolve => {
setTimeout(()=>{
resolve('i am p2')
},2000)
})
Promise.race([p1,p2]).then(
value =>{
console.log(value);
}
)
结果
[Running] node "/home/leondon/Desktop/nodeJsDemo/express-middleware-demo/src/Promise.js"
i am p2
[Done] exited with code=0 in 10.526 seconds
常见用法
- 把异步操作和定时器放在一起
- 如果定时器先触发,就认为超时,告知用户
十七、把回调包装成Promise
把回调包装成Promise最为常见。它有两个显而易见的好处
可读性更好
返回的结果可以加入任何Promise队列
十八、把任意异步操作包装成Promise
假设需求,用户点击按钮,弹出确认窗体
用户确认和取消有不同的处理
样式w恩提不能使用window.confirm()
//伪代码
let confirm = popupManager.confirm('您确定么?')
confirm.promise
.then(()=>{
// do confirm staff
})
.catch(()=>{
//do cancel staff
});
//窗体的构造函数
class Confirm{
constructor(){
this.promise = new Promise((resolve,reject)=>{
this.confirmButton.onClick = resolve;
this.cancelButton.onClick = reject;
})
}
}
十九、异步函数
async / await
- 赋予Javascript以顺序手法编写异步脚本的能力
- 既保留异步运算的无阻塞的特性,还继续使用同步写法
- 还能正常使用 return/try/ catch
- await只能等待一个Promise对象返回
function resolveAfter2Seconds(x){
return new Promise(resolve => {
setTimeout(()=>{
resolve(x)
},2000);
})
}
async function f1(){
var x = await resolveAfter2Seconds(10);
console.log(x);//10
}
f1();