- Promise 的含义
- 基本用法
- 错误进阶
- 应用
- Promise 的实现原理
Promise
是异步编程的一种解决方案,避免了传统的回调函数的层层嵌套,也就是常说的“回调地狱”。
Promise
一旦新建就会立即执行,无法中途取消。
ES6 规定,Promise
对象是一个构造函数,用来生成 Promise
实例。
const promise = new Promise (function (resolve, reject) {
if (/* 异步操作成功*/) {
resolve(value);
} else {
/* 操作失败 */
reject(error);
}
});
Promise
实例生成以后,可以用 then
方法分别指定 resolved
状态和 rejected
状态的回调函数。
promise.then(function(value) {
// success
}, function (error) {
// failure
});
Promise
新建后就会立即执行
let promise = new Promise(function (resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function(){
console.log('resolved');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise
新建后立即执行,所以首先输出的是Promise
。然后,then
方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以 resolved
最后输出。
一般来说,调用 resolve
或 reject
以后,Promise
的使命就完成了,后继操作应该放到 then
方法里面,而不应该直接写在 resolve
或 reject
的后面。所以,最好在它们前面加上 return
语句,这样就不会有意外。
new Promise((resolve, reject) => {
resolve(1);
console.log(2); //虽然此句会执行,但是不建议这样做
}).then(r => {
console.log(r);
});
// 2
// 1
// �resolve 前加 return
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
Promise.resolve()
是一种简化写法。例如如下代码
new Promise(function (resolve, reject) {
resolve(someSynclhronousValue);
}).then(/*...*/);
使用 Promise.resolve
会更加简洁
Promise.resolve(someSynclhronousValue).then(/*...*/);
catch()
仅仅是一个语法糖,下面的代码是等价的:
somePromise().catch(function(err){
// handle error
});
// 等价于
somePromise().then(null, function(err){
// handle error
});
然而,这并不意味着下面两段代码是等价的:
somePromise().then(function(){
return someOtherPromise();
}).catch(function(err){
// handle err
});
// 不等价
somePromise().then(function(){
return someOtherPromise();
}, function(err){
// handle error
});
上面的代码不等价的原因:当使用 then(resolveHandler, rejectHandler)
这种形式时,rejectHandler
并不会捕获由 resolveHandler
引发的异常。例如如下的代码,就很明显看出二者的区别。
somePromise().then(function(){
throw new Error('oh noes');
}).catch(function(err){
// I caught your error!
});
// 下面的代码则捕获不了� error
somePromise().then(function(){
throw new Error('oh noes');
}, function(err){
// I didn't caught your error!
});
一般来说,不要在 then
方法里面定义 Reject
状态的回调函数(即 then
的第二个参数),总是使用 catch
方法。
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
查看一下代码会打印出什么?
Promise.resolve('foo').then(Promise.resolve('bar')).then(function(result){
console.log(result);
});
如果你认为它会打印出 bar
,那么你就错了。它实际上打印出来的是 foo
。
发生这个的原因是如果你向 then()
传递的并非是一个函数(比如 promise
),它实际上会将其解释为 then(null)
,这就会导致前一个 promise
的结果会穿透下面。你可以自己测试一下:
Promise.resolve('foo').then(null).then(function (result) {
console.log(result); // foo
});
添加任意数量的 then(null)
,它依然会打印 foo
。
简单的说,你可以直接传递一个 promise
到 then()
函数中,但是它并不会按照你期望的去执行。then()
是期望获取一个函数,因此你希望做的最可能是:
Promise.resolve('foo').then(function () {
return Promise.resolve('bar');
}).then(function (result) {
console.log(result); // bar
});
这样他就会如我们所想的打印出 bar。
因此记住:永远都是往 then()
中传递函数!
比较以下 4 个 Promise 代码的不同之处。
doSomeThing().then(function () {
return doSomeThingElse();
}).then(finalHandler);
// 第一个 then 中传入的是函数,并且有返回值
// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(resultOfDoSomeThingElse)
doSomeThing().then(function () {
doSomeThingElse();
}).then(finalHandler);
// 第一个 then 中传入的是一个函数,但是 并没有返回值
// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(undefined)
doSomeThing().then(doSomeThingElse()).then(finalHandler);
// 第一个 then 中传入的是一个函数执行的结果,而并非是一个函数,所以 最后的 then 中的参数是 doSomeThing 的执行结果
// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(resultOfDoSomeThing)
doSomeThing().then(doSomeThingElse).then(finalHandler);
// 第一个 then 中传入的是一个函数,所以 最后的 then 中的参数是 doSomeThing 的执行结果
// doSomeThing --> doSomeThingElse(resultOfDoSomeThing) --> finalHandler(resultOfDoSomeThingElse)
function loadImageAsync (url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function () {
reject(new Error('Could not load image at ' + url));
}
image.src = url;
});
}
const getJSON = function (url) {
const promise = new Promise(function(resolve, reject){
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
const handler = function () {
if (this.readyState !== 4) {
return ;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON('/nameList.json').then(function (json) {
console.log('contents' + json);
}, function (error) {
console.error('出错了', error);
});