异步处理方案
回调处理未来值
function add(getX, getY, cb) {
var x, y;
getX(function (xVal) {
console.log('xxxxxxxx',x,y,xVal)
x = xVal;
// 两个都准备好了?
if (y != undefined) {
cb(x + y); // 发送和
}
});
getY(function (yVal) {
console.log('yyyyyyyyyyyyy',x,y,yVal)
y = yVal;
// 两个都准备好了?
if (x != undefined) {
cb(x + y); // 发送和
}
});
}
// fetchX() 和fetchY()是同步或者异步函数
function fetchX(fn) {
console.log(1000, 'minit')
fn(1000)
}
function fetchY(fn) {
setTimeout(() => {
console.log(2000, 'minitiiiiiiii')
fn(2000)
}, 3000)
}
add(fetchX, fetchY, function (sum) {
console.log(sum); // 是不是很容易?
});
// 1000 minit
// xxxxxxxx undefined undefined 1000
// 2000 minitiiiiiiii
// yyyyyyyyyyyyy 1000 undefined 2000
3000
Promise处理未来值
function fetchX() {
return new Promise((res,rej)=>{
res(1000)
})
}
function fetchY() {
return new Promise((res,rej)=>{
setTimeout(() => {
console.log(2000, 'minityyyyyy')
res(2000)
}, 2000)
})
}
function add(xPromise, yPromise) {
// Promise.all([ .. ])接受一个promise数组并返回一个新的promise,
// 这个新promise等待数组中的所有promise完成
return Promise.all([xPromise, yPromise])
// 这个promise决议之后,我们取得收到的X和Y值并加在一起
.then(function (values) {
// values是来自于之前决议的promisei的消息数组
return values[0]+values[1];
});
}
// fetchX()和fetchY()返回相应值的promise,可能已经就绪,
// 也可能以后就绪
add(fetchX(), fetchY())
// 我们得到一个这两个数组的和的promise
// 现在链式调用 then(..)来等待返回promise的决议
.then(function success(sum) {
console.log(sum); // 这更简单!
},function error(sum) {
console.log("异常"); // 这更简单!
});
// 2000 minityyyyyy
// 3000
如果Promise本身决议时间很长,可以使用超时机制提示失败
// 用于超时一个Promise的工具
function timeoutPromise(delay) {
return new Promise( function(resolve,reject){
setTimeout( function(){
reject( "timeoutPromise Timeout!" );
}, delay );
});
}
function foo(){
return new Promise( function(resolve,reject){
setTimeout( function(){
resolve( "foo ok!" );
}, 4000 );
});
}
// 设置foo()超时
Promise.race([
foo(), // 试着开始foo()
timeoutPromise( 3000 ) // 给它3秒钟
]).then(function(value){
// foo(..)及时完成
console.log(value)
},
function(err){
// 或者foo()被拒绝,或者只是没能按时完成
// 查看err来了解是哪种情况
console.log(err)
}
);
// timeoutPromise Timeout!
Promise 的定义方式使得它只能被决议一次。如果出于某种原因. Promise 创建代码试图调用resolve(…) 或 reject(…) 多次,或者试图两者都调用,那么这个 Promise 将只会接受第一次决议,并默默地忽略任何后续调用。由于 Promise 只能被决议一次,所以任何通过 then(…) 注册的(每个)回调就只会被调用一次。
当然,如果你把同一个回调注册了不止一次(比如 p.then(f); p.then(f);),那它被调用的次数就会和注册次数相同。
如果使用多个参数调用 resovle(…) 或者 reject(…),第一个参数之后的所有参数都会被默默忽略。
错误和异常
promise定义时出现异常
var p = new Promise( function(resolve,reject){
foo.bar(); // foo未定义,所以会出错!
resolve( 42 ); // 永远不会到达这里 :(
});
p.then(function fulfilled(succ){
// 永远不会到达这里 :(
console.log("成功")
},
function rejected(err){
// err将会是一个TypeError异常对象来自foo.bar()这一行
console.log("错误")
}
);
// 错误
foo.bar() 中发生的 JavaScript 异常导致了 Promise 拒绝,你可以捕捉并对其作出响应
then中出现异常
var p = new Promise( function(resolve,reject){
resolve( 42 );
});
p.then(function fulfilled(succ){
foo.bar(); // foo未定义,所以会出错!
console.log(succ) // 永远不会到这里
},
function rejected(err){
console.log(err) // 永远不会到这里
}
);
//UnhandledPromiseRejectionWarning: ReferenceError: foo is not defined
Promise 链的一个最佳实践就是最后总以一个 catch(…) 结束。我们没有为 then(…) 传入拒绝处理函数,所以默认的处理函数被替换掉了,而这仅仅是把错误传递给了链中的下一个 promise。因此,进入 p 的错误以及 p 之后进入其决议(就像 msg.toLowerCase())的错误都会传递到最后的 handleErrors(…)
var p = Promise.resolve( 42 );
p.then(
function fulfilled(msg){
// 数字没有string函数,所以会抛出错误
console.log( msg.toLowerCase() );
}
).catch(function(){
console.log("异常")
});
p.then(…) 调用本身返回了另外一个 promise,正是这个 promise 将会因 TypeError 异常而被拒绝
如果向 Promise.resolve(…) 传递一个非 Promise、非 thenable 的立即值,就会得到一个用这个值填充的 promise。下面这种情况下, promise p1 和 promise p2 的行为是完全一样的:
var p1 = new Promise( function(resolve,reject){
resolve( 42 );
});
var p2 = Promise.resolve( 42 );
console.log(p1); // Promise { 42 }
console.log(p2); // Promise { 42 }
而如果向 Promise.resolve(…) 传递一个真正的 Promise,就只会返回同一个 promise:
var p1 = Promise.resolve( 42 );
var p2 = Promise.resolve( p1 );
console.log(p1==p2) // true
Promise.resolve(…) 可以接受任何 thenable,将其解封为它的非 thenable 值。从 Promise.resolve(…) 得到的是一个真正的 Promise,是一个可以信任的值。如果你传入的已经是真正的 Promise,那么你得到的就是它本身,所以通过 Promise.resolve(…) 过滤来获得可信任性完全没有坏处。
var p = {
then: function(cb,errcb) {
cb( 42 );
errcb( "evil laugh" );
}
};
Promise.resolve( p ).then(
function fulfilled(val){
console.log( val ); // 42
},
function rejected(err){
// 啊,不应该运行!
console.log( err ); // 邪恶的笑
}
);
// 42
每次你对 Promise 调用 then(…),它都会创建并返回一个新的 Promise,我们可以将其链接起来;
var p = Promise.resolve( 21 );
p.then( function(v){
console.log( v ); // 21
// 用值42完成连接的promise
return v * 2;
}).then( function(v){
console.log( v ); // 42
});
当传递给 Promise.resolve(…) 的 是 一 个 Promise 或 thenable 而 不 是 最 终 值 时 的 运 作 方 式。 Promise.resolve(…) 会直接返回接收到的真正 Promise,或展开接收到的 thenable 值,并在持续展
开 thenable 的同时递归地前进。
从完成(或拒绝)处理函数返回 thenable 或者 Promise 的时候也会发生同样的展开
var p = Promise.resolve( 21 );
p.then( function(v){
console.log( v ); // 21
// 创建一个promise并将其返回
return new Promise( function(resolve,reject){
// 用值42填充
resolve( v * 2 );
});
}).then( function(v){
console.log( v ); // 42
});
调用 Promise 的 then(…) 会自动创建一个新的 Promise 从调用返回。
在完成或拒绝处理函数内部,如果返回一个值或抛出一个异常,新返回的(可链接的)Promise 就相应地决议。
如果完成或拒绝处理函数返回一个 Promise,它将会被展开,这样一来,不管它的决议值是什么,都会成为当前 then(…) 返回的链接 Promise 的决议值
同步的 try…catch 结构。它只能是同步的,无法用于异步代码模式:
function foo() {
setTimeout( function(){
baz.bar();
}, 100 );
}
try {
foo();
// 后面从 `baz.bar()` 抛出全局错误
}catch (err) {
// 永远不会到达这里
console.log("错误")
}
只有在 baz.bar() 调用会同步地立即成功或失败的情况下,这里的 try…catch 才能工作。如果 baz.bar() 本身有自己的异步完成函数,其中的任何异步错误都将无法捕捉到。
function foo(cb) {
setTimeout( function(){
try {
var x = baz.bar();
cb( null, x ); // 成功!
}catch (err) {
cb( err );
}}, 100 );
}
foo(function(err,val){
if (err) {
console.error( "错误" ); // 错误
}else {
console.log( val );
}
});