resolve()和reject都是直接生成一个进入相应状态的promise对象,其参数就是进入相应状态时传递过去的参数,可以在完成回调的参数中得到
Promise.resolve(value),Promise.reject(reason)是Promise构造器上直接提供的一组静态方法
在调用then方法或者catch方法时都是异步进行的,但是执行速度比较快
Promise构造器的prototype上还有两个方法,分别是then和catch。这两个方法的参数也是回调函数,这些函数会在Promise实例进入不同状态后被调用。then对应到resolve,catch对应到reject
JS 中最基础的异步调用方式是 callback,它将回调函数 callback 传给异步 API,由浏览器或 Node 在异步完成后,通知 JS 引擎调用 callback。对于简单的异步操作,用 callback 实现,是够用的。但随着负责交互页面和 Node 出现,callback 方案的弊端开始浮现出来。 Promise 规范孕育而生,并被纳入 ES6 的规范中。后来 ES7 又在 Promise 的基础上将 async 函数纳入标准。此为 JavaScript 异步进化史。
通常,代码是由上往下依次执行的。如果有多个任务,就必需排队,前一个任务完成,后一个任务才会执行。这种执行模式称之为:同步(synchronous)。新手容易把计算机用语中的同步,和日常用语中的同步弄混淆。如,“把文件同步到云端”中的同步,指的是“使...保持一致”。而在计算机中,同步指的是任务从上往下依次执行的模式。比如:
A();
B();
C();
在这段代码中,A、B、C是三个不同的函数,每个函数都是一个不相关的任务。在同步模式,计算机会先执行 A 任务,再执行 B 任务,最后执行 C 任务。在大部分情况,同步模式都没问题。但是如果 B 任务是一个耗时很长的网络请求,而 C 任务恰好是展现新页面,就会导致网页卡顿。
更好解决方案是,将 B 任务分成两个部分。一部分立即执行网络请求的任务,另一部分在请求回来后的执行任务。这种一部分立即执行,另一部分在未来执行的模式称为异步。
A();
// 在现在发送请求
ajax('url1',function B() {
// 在未来某个时刻执行
})
C();
// 执行顺序 A => C => B
实际上,JS 引擎并没有直接处理网络请求的任务,它只是调用了浏览器的网络请求接口,由浏览器发送网络请求并监听返回的数据。JavaScript 异步能力的本质是浏览器或 Node 的多线程能力。
试着判断下面这段代码的执行顺序,你会对“非线性”代码比“线性”代码更难以阅读,体会更深。
A();
ajax('url1', function(){
B();
ajax('url2', function(){
C();
}
D();
});
E();
// A => E => B => D => C
这段代码中,从上往下执行的顺序被 Callback 打乱了。我们的阅读代码视线是A => B => C => D => E,但是执行顺序却是A => E => B => D => C,这就是非线性代码带来的糟糕之处。
通过将ajax后面执行的任务提前,可以更容易看懂代码的执行顺序。虽然代码因为嵌套看起来不美观,但现在的执行顺序却是从上到下的“线性”方式。这种技巧在写多重嵌套的代码时,是非常有用的。
A();
E();
ajax('url1', function(){
B();
D();
ajax('url2', function(){
C();
}
});
// A => E => B => D => C
上一段代码只有处理了成功回调,并没处理异常回调。接下来,把异常处理回调加上,再来讨论代码“线性”执行的问题。
A();
ajax('url1', function(){
B();
ajax('url2', function(){
C();
},function(){
D();
});
},function(){
E();
});
加上异常处理回调后,url1的成功回调函数 B 和异常回调函数 E,被分开了。这种“非线性”的情况又出现了。
在 node 中,为了解决的异常回调导致的“非线性”的问题,制定了错误优先的策略。node 中 callback 的第一个参数,专门用于判断是否发生异常
A();
get('url1', function(error){
if(error){
E();
}else {
B();
get('url2', function(error){
if(error){
D();
}else{
C();
}
});
}
});
到此,callback 引起的“非线性”问题基本得到解决。遗憾的是,使用 callback 嵌套,一层层if else和回调函数,一旦嵌套层数多起来,阅读起来不是很方便。此外,callback 一旦出现异常,只能在当前回调函数内部处理异常。
在 JavaScript 的异步进化史中,涌现出一系列解决 callback 弊端的库,而 Promise 成为了最终的胜者,并成功地被引入了 ES6 中。它将提供了一个更好的“线性”书写方式,并解决了异步异常只能在当前回调中被捕获的问题。
Promise 就像一个中介,它承诺会将一个可信任的异步结果返回。首先 Promise 和异步接口签订一个协议,成功时,调用resolve函数通知 Promise,异常时,调用reject通知 Promise。另一方面 Promise 和 callback 也签订一个协议,由 Promise 在将来返回可信任的值给then和catch中注册的 callback。
// 创建一个 Promise 实例(异步接口和 Promise 签订协议)
var promise = new Promise(function (resolve,reject) {
ajax('url',resolve,reject);
});
// 调用实例的 then catch 方法 (成功回调、异常回调与 Promise 签订协议)
promise.then(function(value) {
// success
}).catch(function (error) {
// error
})
Promise 是个非常不错的中介,它只返回可信的信息给 callback。它对第三方异步库的结果进行了一些加工,保证了 callback 一定会被异步调用,且只会被调用一次。
var promise1 = new Promise(function (resolve) {
// 可能由于某些原因导致同步调用
resolve('B');
});
// promise依旧会异步执行
promise1.then(function(value){
console.log(value)
});
console.log('A');
// A B (先 A 后 B)
var promise2 = new Promise(function (resolve) {
// 成功回调被通知了2次
setTimeout(function(){
resolve();
},0)
});
// promise只会调用一次
promise2.then(function(){
console.log('A')
});
// A (只有一个)
var promise3 = new Promise(function (resolve,reject) {
// 成功回调先被通知,又通知了失败回调
setTimeout(function(){
resolve();
reject();
},0)
});
// promise只会调用成功回调
promise3.then(function(){
console.log('A')
}).catch(function(){
console.log('B')
});
// A(只有A)
介绍完 Promise 的特性后,来看看它如何利用链式调用,解决异步代码可读性的问题的。
var fetch = function(url){
// 返回一个新的 Promise 实例
return new Promise(function (resolve,reject) {
ajax(url,resolve,reject);
});
}
A();
fetch('url1').then(function(){
B();
// 返回一个新的 Promise 实例
return fetch('url2');
}).catch(function(){
// 异常的时候也可以返回一个新的 Promise 实例
return fetch('url2');
// 使用链式写法调用这个新的 Promise 实例的 then 方法
}).then(function() {
C();
// 继续返回一个新的 Promise 实例...
})
// A B C ...
如此反复,不断返回一个 Promise 对象,再采用链式调用的方式不断地调用。使 Promise 摆脱了 callback 层层嵌套的问题和异步代码“非线性”执行的问题。
Promise 解决的另外一个难点是 callback 只能捕获当前错误异常。Promise 和 callback 不同,每个 callback 只能知道自己的报错情况,但 Promise ***着所有的 callback,所有 callback 的报错,都可以由 Promise 统一处理。所以,可以通过catch来捕获之前未捕获的异常。
Promise 解决了 callback 的异步调用问题,但 Promise 并没有摆脱 callback,它只是将 callback 放到一个可以信任的中间机构,这个中间机构去链接我们的代码和异步接口。
异步(async)函数是 ES7 的一个新的特性,它结合了 Promise,让我们摆脱 callback 的束缚,直接用类同步的“线性”方式,写异步函数。
声明异步函数,只需在普通函数前添加一个关键字 async 即可,如async function main(){} 。在异步函数中,可以使用await关键字,表示等待后面表达式的执行结果,一般后面的表达式是 Promise 实例。
async function main{
// timer 是在上一个例子中定义的
var value = await timer(100);
console.log(value); // done (100ms 后返回 done)
}
main();
异步函数和普通函数一样调用 main() 。调用后,会立即执行异步函数中的第一行代码 var value = await timer(100)。等到异步执行完成后,才会执行下一行代码。
除此之外,异步函数和其他函数基本类似,它使用try...catch来捕捉异常。也可以传入参数。但不要在异步函数中使用return来返回值。
var timer = new Promise(function create(resolve,reject) {
if(typeof delay !== 'number'){
reject(new Error('type error'));
}
setTimeout(resolve,delay,'done');
});
async function main(delay){
try{
var value1 = await timer(delay);
var value2 = await timer('');
var value3 = await timer(delay);
}catch(err){
console.error(err);
// Error: type error
// at create (<anonymous>:5:14)
// at timer (<anonymous>:3:10)
// at A (<anonymous>:12:10)
}
}
main(0);
异步函数也可以被当作值,传入普通函数和异步函数中执行。但是在异步函数中,使用异步函数时要注意,如果不使用await,异步函数会被同步执行。
async function main(delay){
var value1 = await timer(delay);
console.log('A')
}
async function doAsync(main){
main(0);
console.log('B')
}
doAsync(main);
// B A
这个时候打印出来的值是 B A。说明 doAsync 函数并没有等待 main 的异步执行完毕就执行了 console。如果要让 console 在 main 的异步执行完毕后才执行,我们需要在main前添加关键字await。
async function main(delay){
var value1 = await timer(delay);
console.log('A')
}
async function doAsync(main){
await main(0);
console.log('B')
}
doAsync(main);
// A B
由于异步函数采用类同步的书写方法,所以在处理多个并发请求,新手可能会像下面一样书写。这样会导致url2的请求必需等到url1的请求回来后才会发送。
var fetch = function (url) {
return new Promise(function (resolve,reject) {
ajax(url,resolve,reject);
});
}
async function main(){
try{
var value1 = await fetch('url1');
var value2 = await fetch('url2');
conosle.log(value1,value2);
}catch(err){
console.error(err)
}
}
main();
使用Promise.all的方法来解决这个问题。Promise.all用于将多个Promise实例,包装成一个新的 Promis e实例,当所有的 Promise 成功后才会触发Promise.all的resolve函数,当有一个失败,则立即调用Promise.all的reject函数。
var fetch = function (url) {
return new Promise(function (resolve,reject) {
ajax(url,resolve,reject);
});
}
async function main(){
try{
var arrValue = await Promise.all[fetch('url1'),fetch('url2')];
conosle.log(arrValue[0],arrValue[1]);
}catch(err){
console.error(err)
}
}
main();
目前使用 Babel 已经支持 ES7 异步函数的转码了,大家可以在自己的项目中开始尝试。
via:https://segmentfault.com/a/1190000006138882
"Then对应到resolve"错误,then第一个参数对应resolve,第二个可有可无,如果有第二个参数,那么第二个参数对应rejeted.catch对应rejected
then()方法返回一个Promise。它有两个参数,分别为Promise在 success 和 failure 情况下的回调函数。 p.then(function(value) { // 满足 }, function(reason) { // 拒绝 }); onFulfilled 一个 Function, 当 Promise 为 fulfilled 时调用. 该函数有一个参数, 为成功的返回值. onRejected 一个 Function, 当 Promise 为 rejected 时调用. 该函数有一个参数, 为失败的原因.
p.then(onFulfilled, onRejected);
通常,代码是由上往下依次执行的。如果有多个任务,就必需排队,前一个任务完成,后一个任务才会执行。这种执行模式称之为:同步(synchronous)。新手容易把计算机用语中的同步,和日常用语中的同步弄混淆。如,“把文件同步到云端”中的同步,指的是“使...保持一致”。而在计算机中,同步指的是任务从上往下依次执行的模式。比如:
A(); B(); C();
在这段代码中,A、B、C是三个不同的函数,每个函数都是一个不相关的任务。在同步模式,计算机会先执行 A 任务,再执行 B 任务,最后执行 C 任务。在大部分情况,同步模式都没问题。但是如果 B 任务是一个耗时很长的网络请求,而 C 任务恰好是展现新页面,就会导致网页卡顿。
更好解决方案是,将 B 任务分成两个部分。一部分立即执行网络请求的任务,另一部分在请求回来后的执行任务。这种一部分立即执行,另一部分在未来执行的模式称为异步。
A(); // 在现在发送请求 ajax('url1',function B() { // 在未来某个时刻执行 }) C(); // 执行顺序 A => C => B
实际上,JS 引擎并没有直接处理网络请求的任务,它只是调用了浏览器的网络请求接口,由浏览器发送网络请求并监听返回的数据。JavaScript 异步能力的本质是浏览器或 Node 的多线程能力。
试着判断下面这段代码的执行顺序,你会对“非线性”代码比“线性”代码更难以阅读,体会更深。
A(); ajax('url1', function(){ B(); ajax('url2', function(){ C(); } D(); }); E(); // A => E => B => D => C
这段代码中,从上往下执行的顺序被 Callback 打乱了。我们的阅读代码视线是A => B => C => D => E,但是执行顺序却是A => E => B => D => C,这就是非线性代码带来的糟糕之处。
通过将ajax后面执行的任务提前,可以更容易看懂代码的执行顺序。虽然代码因为嵌套看起来不美观,但现在的执行顺序却是从上到下的“线性”方式。这种技巧在写多重嵌套的代码时,是非常有用的。
A(); E(); ajax('url1', function(){ B(); D(); ajax('url2', function(){ C(); } }); // A => E => B => D => C
上一段代码只有处理了成功回调,并没处理异常回调。接下来,把异常处理回调加上,再来讨论代码“线性”执行的问题。
A(); ajax('url1', function(){ B(); ajax('url2', function(){ C(); },function(){ D(); }); },function(){ E(); });
加上异常处理回调后,url1的成功回调函数 B 和异常回调函数 E,被分开了。这种“非线性”的情况又出现了。
在 node 中,为了解决的异常回调导致的“非线性”的问题,制定了错误优先的策略。node 中 callback 的第一个参数,专门用于判断是否发生异常
A(); get('url1', function(error){ if(error){ E(); }else { B(); get('url2', function(error){ if(error){ D(); }else{ C(); } }); } });
到此,callback 引起的“非线性”问题基本得到解决。遗憾的是,使用 callback 嵌套,一层层if else和回调函数,一旦嵌套层数多起来,阅读起来不是很方便。此外,callback 一旦出现异常,只能在当前回调函数内部处理异常。
在 JavaScript 的异步进化史中,涌现出一系列解决 callback 弊端的库,而 Promise 成为了最终的胜者,并成功地被引入了 ES6 中。它将提供了一个更好的“线性”书写方式,并解决了异步异常只能在当前回调中被捕获的问题。
Promise 就像一个中介,它承诺会将一个可信任的异步结果返回。首先 Promise 和异步接口签订一个协议,成功时,调用resolve函数通知 Promise,异常时,调用reject通知 Promise。另一方面 Promise 和 callback 也签订一个协议,由 Promise 在将来返回可信任的值给then和catch中注册的 callback。
// 创建一个 Promise 实例(异步接口和 Promise 签订协议) var promise = new Promise(function (resolve,reject) { ajax('url',resolve,reject); }); // 调用实例的 then catch 方法 (成功回调、异常回调与 Promise 签订协议) promise.then(function(value) { // success }).catch(function (error) { // error })
Promise 是个非常不错的中介,它只返回可信的信息给 callback。它对第三方异步库的结果进行了一些加工,保证了 callback 一定会被异步调用,且只会被调用一次。
var promise1 = new Promise(function (resolve) { // 可能由于某些原因导致同步调用 resolve('B'); }); // promise依旧会异步执行 promise1.then(function(value){ console.log(value) }); console.log('A'); // A B (先 A 后 B) var promise2 = new Promise(function (resolve) { // 成功回调被通知了2次 setTimeout(function(){ resolve(); },0) }); // promise只会调用一次 promise2.then(function(){ console.log('A') }); // A (只有一个) var promise3 = new Promise(function (resolve,reject) { // 成功回调先被通知,又通知了失败回调 setTimeout(function(){ resolve(); reject(); },0) }); // promise只会调用成功回调 promise3.then(function(){ console.log('A') }).catch(function(){ console.log('B') }); // A(只有A)
介绍完 Promise 的特性后,来看看它如何利用链式调用,解决异步代码可读性的问题的。
var fetch = function(url){ // 返回一个新的 Promise 实例 return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } A(); fetch('url1').then(function(){ B(); // 返回一个新的 Promise 实例 return fetch('url2'); }).catch(function(){ // 异常的时候也可以返回一个新的 Promise 实例 return fetch('url2'); // 使用链式写法调用这个新的 Promise 实例的 then 方法 }).then(function() { C(); // 继续返回一个新的 Promise 实例... }) // A B C ...
如此反复,不断返回一个 Promise 对象,再采用链式调用的方式不断地调用。使 Promise 摆脱了 callback 层层嵌套的问题和异步代码“非线性”执行的问题。
Promise 解决的另外一个难点是 callback 只能捕获当前错误异常。Promise 和 callback 不同,每个 callback 只能知道自己的报错情况,但 Promise ***着所有的 callback,所有 callback 的报错,都可以由 Promise 统一处理。所以,可以通过catch来捕获之前未捕获的异常。
Promise 解决了 callback 的异步调用问题,但 Promise 并没有摆脱 callback,它只是将 callback 放到一个可以信任的中间机构,这个中间机构去链接我们的代码和异步接口。
异步(async)函数是 ES7 的一个新的特性,它结合了 Promise,让我们摆脱 callback 的束缚,直接用类同步的“线性”方式,写异步函数。
声明异步函数,只需在普通函数前添加一个关键字 async 即可,如async function main(){} 。在异步函数中,可以使用await关键字,表示等待后面表达式的执行结果,一般后面的表达式是 Promise 实例。
async function main{ // timer 是在上一个例子中定义的 var value = await timer(100); console.log(value); // done (100ms 后返回 done) } main();
异步函数和普通函数一样调用 main() 。调用后,会立即执行异步函数中的第一行代码 var value = await timer(100)。等到异步执行完成后,才会执行下一行代码。
除此之外,异步函数和其他函数基本类似,它使用try...catch来捕捉异常。也可以传入参数。但不要在异步函数中使用return来返回值。
var timer = new Promise(function create(resolve,reject) { if(typeof delay !== 'number'){ reject(new Error('type error')); } setTimeout(resolve,delay,'done'); }); async function main(delay){ try{ var value1 = await timer(delay); var value2 = await timer(''); var value3 = await timer(delay); }catch(err){ console.error(err); // Error: type error // at create (<anonymous>:5:14) // at timer (<anonymous>:3:10) // at A (<anonymous>:12:10) } } main(0);
异步函数也可以被当作值,传入普通函数和异步函数中执行。但是在异步函数中,使用异步函数时要注意,如果不使用await,异步函数会被同步执行。
async function main(delay){ var value1 = await timer(delay); console.log('A') } async function doAsync(main){ main(0); console.log('B') } doAsync(main); // B A
这个时候打印出来的值是 B A。说明 doAsync 函数并没有等待 main 的异步执行完毕就执行了 console。如果要让 console 在 main 的异步执行完毕后才执行,我们需要在main前添加关键字await。
async function main(delay){ var value1 = await timer(delay); console.log('A') } async function doAsync(main){ await main(0); console.log('B') } doAsync(main); // A B
由于异步函数采用类同步的书写方法,所以在处理多个并发请求,新手可能会像下面一样书写。这样会导致url2的请求必需等到url1的请求回来后才会发送。
var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } async function main(){ try{ var value1 = await fetch('url1'); var value2 = await fetch('url2'); conosle.log(value1,value2); }catch(err){ console.error(err) } } main();
使用Promise.all的方法来解决这个问题。Promise.all用于将多个Promise实例,包装成一个新的 Promis e实例,当所有的 Promise 成功后才会触发Promise.all的resolve函数,当有一个失败,则立即调用Promise.all的reject函数。
var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } async function main(){ try{ var arrValue = await Promise.all[fetch('url1'),fetch('url2')]; conosle.log(arrValue[0],arrValue[1]); }catch(err){ console.error(err) } } main();
目前使用 Babel 已经支持 ES7 异步函数的转码了,大家可以在自己的项目中开始尝试。