Promise按照用途来解释:
主要用于异步运算。
可以将异步操作队列化,按照期望的顺序执行,返回符合期望的结果。
可以在对象之间传递和操作Promise,帮助我们处理队列。
Promise产生的原因:
Javascript中包含大量异步操作。
创造它的首要目标是操作DOM。
所以,Javascript的操作大多是异步的。
浏览器中的Javascript
异步操作以 事件为主。
回调主要出现在Ajax和File API 。
异步回调的问题:
嵌套层次很深,难以维护。
无法正常使用return和throw。
无法正常检索堆栈信息。
多个回调之间难以建立联系。
下面是Promise的模型简介:
new Promise(
/*执行器executor*/
function (resovle, reject) {
resovle();
reject();
}
)
.then(function A() {
//成功,下一步
}, function B() {
//失败,做响应处理
})
从上面代码可以看出:
Promise 是一个代理对象,它和原先要进行的操作并无关系。
它通过引入一个回调,避免更多的回调。
Promise有3个状态:
- pendding[待定] 初始状态
- fulfilled[实现]操作成功
- rejected[被否决]操作失败
当Promise状态发生变化 ,就会触发.then() 里的相应函数处理后续步骤。
Promise状态一经改变,不会再变。
接下来这张图片是Promise执行的过程
每个then执行完了之后,都会返回一个Promise对象,然后再调用下一个then,直到所有的then都处理完。
最简单的实例:
console.log('here we go');
new Promise(resolve => {
console.log("execute start");
setTimeout(() => {
resolve('hello');
}, 2000)
}).then(value => {
console.log(value + ' resolve world');
});
代码的执行结果为
here we go
execute start
hello resolve world
这样可以直观的看出then函数是等执行器执行结束才会执行的。
两步执行的范例:分2次,顺序依次执行
console.log('here we go');
new Promise(resolve => {
console.log("execute start");
setTimeout(() => {
resolve('hello');
}, 2000)
}).then(value => {
console.log(value + ' resolve world');
return new Promise(resolve => {
setTimeout(() => {
resolve('world');
}, 2000)
})
}).then(value => {
console.log(value + ' world');
});
代码的执行结果为:
here we go
execute start
hello resolve world
world world
从上面例子可以看出,promise是会顺序执行每个then的。
当promise里面的状态一旦发生变化,会调用then,
然后then会生成一个新的promise,每次promise其实传递的只有一个状态,(resolve或者reject),
紧接着then会接收到这个状态函数里面传递的数值。
promise用自己的回调解决了多个回调函数嵌套的问题
假设一个Promise已经完成了,再.then()会怎样?
console.log('start');
let promise = new Promise(resolve => {
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello, world');
}, 1000);
});
setTimeout(() => {
promise.then(value => {
console.log(value);
})
}, 3000)
输出结果为:
start
the promise fulfilled
hello, world
在任何一个地方生成了promise队列后,如果我们的队列是先进先出的状态,队列会按顺序执行,后面追加的then,也会接受到前面promise返回的值。
假设在.then()的函数里面不返回新的Promise,会怎样?
console.log('start');
new Promise(resolve => {
setTimeout(() => {
resolve('hello');
})
})
.then(value => {
console.log(value);
console.log('everyone');
(function () {
return new Promise(resolve => {
setTimeout(() => {
console.log('Mr laure');
resolve('Merry XmaS');
}, 2000);
})
}())
return false;
})
.then(value => {
console.log(value + ' world');
});
输出结果为:
start
hello
everyone
false worldMr laure
上面的代码可以看出,第一个then函数里面有一个立即执行的函数,是在2s之后返回一个promise对象,但是先返回了false,就会调用第2个then;
因为第2个then没有返回新的值,返回的promise是在立即执行的函数里返回的,而不是在then的响应函数里面返回的,then返回的promise实例就没有等待里面的函数完成,而是在返回了false之后,直接调用了下面那个then,false被传递给了第2个then函数。
在promise里面,如果不直接返回一个promise实例,会默认执行下一个环节,即使返回了false,如果没有传递值,第2个then里面的value就是undefined,仍然不影响promise往下执行。
引出.then()
.then()接收两个函数作为参数,分别代表fulfilled和rejected
.then()返回一个新的Promise实例,所以它可以链式调用
当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行。
状态响应函数可以返回新的Promise,或者其他值。
如果返回新的Promise,那么下一级.then() 会在新Promise状态改变之后执行。
如果返回其他任何值,则会立刻指向下一级.then()
then的嵌套
因为.then()返回的还是Promise实例。
会等里面的.then()执行完,再执行外面的。
对于我们来说,此时最好将其展开,会更好读。
如下代码:then里面又嵌套几个then
console.log('start');
new Promise(resolve => {
setTimeout(() => {
resolve(100);
}, 1000)
})
.then(value => {
return new Promise(resolve => {
console.log('1-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 + ' world');
});
输出结果为:
start
1-1
step 1-2
step 1-3
110 world
看结果可以得出,then里面嵌套的then都是逐步执行的,value会一级一级往下传递,但是不建议then里面嵌套then的方式来写代码,这样感觉看起来不是很友好,展开then里面的then(),下面的方式会友好 一点。
console.log('start');
new Promise(resolve => {
setTimeout(() => {
resolve(100);
}, 1000)
})
.then(value => {
return new Promise(resolve => {
console.log('1-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 + ' world');
});
接下来,看看下面四种Promise的区别:
错误处理
Promise会自动捕获内部异常,并交给rejected响应函数处理。
console.log('start');
new Promise(resolve => {
setTimeout(() => {
throw new Error('bye');
}, 1000);
})
.then(value => {
console.log(value + 'world')
})
.catch(error => {
console.log('Error:', error.message);
});
console.log('start');
new Promise(resolve => {
setTimeout(() => {
throw new Error('bye');
}, 1000);
})
.then(value => {
console.log(value + 'world')
}, value => {
console.log('Error: ', value);
});
推荐用第一种方式,用catch的方式来整体捕获错误。
错误和then连用
catch也会返回promise实例,如果返回的过程中没有发生错误的时候,下面的then会依次执行,如果抛出了异常,下面的then会忽略
注意:强烈建议在所有队列最后都加上.catch(),以避免漏掉错误处理造成意想不到的问题。
Promise常用函数:
Promise.all() 函数和.map()函数连用