异步回调的问题
- 层次嵌套很深,难以维护
- 无法正常的使用return和throw
- 无法正常的检索堆栈信息 (每次回调执行都是在系统层面上新的堆栈)
- 多个回调之间难以建立联系
Promise的设计
new Promise(
/* 执行器 executor */
function (resolve, reject) {
/* 一段耗时很长的异步操作 */
resolve(); // 数据处理完成
reject(); // 数据处理出错
}
)
.then(function A() {
// 成功,下一步
}, function B() {
// 失败,做相应处理
});
- Promise是一个代理对象,它和原先要进行的异步操作并无关系,只是把这段耗时很长的异步操作放在执行器中;
- 通过引入一个回调,避免更多的回调;
- Promise有3个状态:
-pending
[待定] 初始状态
-fulfilled
[实现] 操作成功
-rejected
[被否决] 操作失败 - 当Promise状态一旦发生改变,就会触发对应
.then
里的响应函数来处理后续步骤; - Promise状态一经改变,就不会再变;
-
Promise实例一经创建,执行器立即执行;
- 定时执行
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log( value + ' world');
});
输出结果:
here we go
Promise {<pending>}
(约2s后)hello 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 + ' world');
});
输出结果:
here we go
Promise {<pending>}
(约2s后)hello
(约2s后)world world
- 对已完成的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
74
(约1s后)the promise fulfilled
(约2s后)hello, world
在任何地方生成一个Promise队列后,可以把它作为一个变量传到其他地方,不管这个Promise状态是否是完成状态,队列都会依次执行;若为完成,会依次执行Promise内的执行器部分,若已完成,后面追加的.then
将会得到Promise返回的值
-
then()
里不返回Promise
console.log('here we go');
new Promise(resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log(value);
console.log('everyone');
(function () {
return new Promise(resolve => {
setTimeout(() => {
console.log('Mr.Laurence');
resolve('Merry Xmas');
}, 2000);
});
}());
return false;
})
.then( value => {
console.log(value + ' world');
});
输出结果:
here we go
Promise {<pending>}
hello
everyone
false world
(约2s后)Mr.Laurence
在这里,第一个then()
里相应函数返回的Promise并不会等待新创建的Promise实例执行(那段立即函数,陷阱!),即使then()
响应函数返回值是false
;也就是说,在Promise里即使没有直接返回一个Promise实例,也会默认去执行下一个环节。这里将return false
注释掉,可以发现,会默认返回undefined
值,并传递给下一个then()
的相应函数中。
下面介绍一下.then()
- 接收两个函数作为参数,分别代表
fulfilled
和rejected
-
.then()
返回一个新的Promise实例,所以它可以链式调用 - 当前面的Promise状态改变时,
.then()
会根据其最终状态选择相应的响应函数进行处理 - 状态响应函数可以返回一个新的Promise实例,或其它值,不返回则为
undefined
- 如果返回新的Promise实例,那么下一级
.then()
会在新Promise状态发生改变后执行 - 如果返回其它值,则会立刻执行下一级
.then()
避免嵌套.then()
因为.then()
返回的还是Promise实例,会等里面的.then()
执行完,才执行外面的。对于我们来说,此时最好将其展开,避免then()
函数里嵌套then()
,也方便阅读。
四道题目
原问题地址和译文地址请戳←
假设doSomething和doSomethingElse返回的都是一个Promise实例,下面的四种 promises 的区别是什么?
- 问题一
doSomething()
.then(function () {
return doSomethingElse();
})
.then(finalHandler);
答案:
(最常见的Promise形式,then()
相应函数返回一个新的Promise实例)
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|
- 问题二
doSomething()
.then(function () {
doSomethingElse();
})
.then(finalHandler);
答案:
(第一个then()
响应函数没有直接返回一个新的Promise实例,也就是说doSomethingElse()
返回的Promise实例并没有返回给then()
的响应函数,此时默认返回undefined
,并且finalHandler
会立刻执行几乎会和doSomethingElse ()
同时)
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |------------------|
// finalHandler(undefined)
// |------------------|
- 问题三
doSomething()
.then(doSomethingElse())
.then(finalHandler);
答案:
(第一个then()
函数中传入的是一个函数,且采用的是函数执行的方式;实际上传入的是一个Promise实例。这种情况下,doSomethingElse
和doSomething
的执行可以看作几乎是同时的,因为是在同一个栈中执行的)。
在Promise的规范中,then()
中传入的若不是一个函数,这个then()
就会被忽略;在这里doSomethingElse
执行完返回的是一个Promise。也就是说,finalHandler
监听的是doSomething
的完成时间,在doSomething
完成后,finalHandler
就会被执行。
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |----------------------------------|
// finalHandler(resultOfDoSomething)
// |------------------|
- 问题四
doSomething()
.then(doSomethingElse)
.then(finalHandler);
答案:
(then()
接收两个函数作为参数,第一个是fulfilled
状态,第二个是rejected
状态);在这里doSomethingElse
作为fulfilled
状态的响应函数对其进行后续处理
// doSomething
// |-----------|
// doSomethingElse(resultOfDoSomething)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------------|
关于错误处理
Promise会自动捕获内部异常(即在执行器当中如果发生错误或自己抛出错误,Promise的状态就会被改为rejected
),随后调用rejected
响应函数进行处理,也会向后面寻找catch()
响应函数进行处理。
- catch捕获
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
throw new Error('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
})
.catch( error => {
console.log( 'Error:', error.message);
});
输出结果:(fulfilled
响应函数不会得到执行)
here we go
Promise {<pending>}
(约2s后)Uncaught Error: bye
at setTimeout (<anonymous>:4:15)
- reject响应捕获
console.log('here we go');
new Promise( (resolve, reject) => {
setTimeout( () => {
reject('bye');//throw new Error('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
}, value => {
console.log( 'Error:', value);
});
推荐使用第一种,更加清晰好读,并且可以捕获前面所有.then()
的错误。
关于catch()
实际上只是 then(null, ...)
的语法糖
console.log('here we go');
new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
})
.then( () => {
console.log('start');
throw new Error('test error');
})
.catch( err => {
console.log('I catch:', err);
// 下面这一行的注释将引发不同的走向
// throw new Error('another error');
})
.then( () => {
console.log('arrive here');
})
.then( () => {
console.log('... and here');
})
.catch( err => {
console.log('No, I catch:', err);
});
输出结果:
here we go
Promise {<pending>}
(约1s后)start
I catch: Error: test error
at Promise.then (<anonymous>:10:15)
at <anonymous>
arrive here
and here
可以看出,catch()
也返回一个Promise实例,并且catch()
内部没有抛出错误时,返回的这个Promise实例是fulfilled
状态,所以接下来的then()
都会被执行。
注意:
建议在所有队列最后都加上catch()
,以避免漏掉错误处理造成意想不到的问题。