Promise 对象

Promise 对象必须实现then方法,then方法接受两个回调函数,分别为:成功时的回调和失败时的回调。

Promise 对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。 但 Promise 一旦新建就会立即执行,无法中途取消。如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

立即执行性

var p = new Promise(function(resolve, reject){
  console.log("create a promise");
  resolve("success");
});

console.log("after new Promise");

p.then(function(value){
  console.log(value);
});

输出
create a promise
after new Promise
success

Promise 对象表示未来某个将要发生的事件,但在创建(new)Promise 时,作为 Promise 参数传入的函数是会被立即执行的,只是其中执行的代码可以是异步代码。有些同学会认为,当Promise对象调用then方法时,Promise接收的函数才会执行,这是错误的。

回调异步性

var p = new Promise(function(resolve, reject){
  resolve("success");
});

p.then(function(value){
  console.log(value);
});

console.log("which one is called first ?");

输出
"which one is called first ?"
"success"

Promise 接收的函数参数是同步执行的,但 then 方法中的回调函数执行则是异步的,因此,”success” 会在后面输出。

状态不可逆

Promise 对象有三种状态: Pending – Promise对象的初始状态,等到任务的完成或者被拒绝; Resolved – 任务执行完成并且成功的状态; Rejected – 任务执行完成并且失败的状态;

Promise 的内部实现是一个状态机。当 Promise 刚创建完成时,处于 pending 状态;当 Promise 中的函数参数执行了 resolve 后,Promise 由 pending 状态变成 resolved 状态;如果在 Promise 的函数参数中执行的不是 resolve 方法,而是 reject 方法,那么Promise 会由 pending 状态变成 rejected 状态。只有异步操作结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

Promise状态的一旦变成 resolved 或 rejected 时,Promise 的状态和值就固定下来了,不论后续再怎么调用 resolve 或 reject 方法,都不能改变它的状态和值。因此,p1中 resolve(“success2”) 并不能将 p1 的值更改为 success2,p2 中 reject(“reject”) 也不能将 p2 的状态由 resolved 改变为 rejected.

var p1 = new Promise(function(resolve, reject){
  resolve("success1");
  resolve("success2");
});

var p2 = new Promise(function(resolve, reject){
  resolve("success");
  reject("reject");
});

p1.then(function(value){
  console.log(value);
});

p2.then(function(value){
  console.log(value);
}); 

输出
"success1"
"success"

链式调用

var p = new Promise(function(resolve, reject){
  resolve(1);
});
p.then(function(value){               //第一个then
  console.log(value);
  return value*2;
}).then(function(value){              //第二个then
  console.log(value);
}).then(function(value){              //第三个then
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){              //第四个then
  console.log(value);
  return Promise.reject('reject');
}).then(function(value){              //第五个then
  console.log('resolve: '+ value);
}, function(err){
  console.log('reject: ' + err);
})

输出
1
2
undefined
"resolve"
"reject: reject"

因为 Promise 对象的 then 方法会返回一个新的 Promise 对象,因此可以通过链式调用 then 方法。then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用于 then 返回的 Promise 对象中。

  • return 一个同步的值 ,或者 undefined(当没有返回一个有效值时,默认返回 undefined ),then 方法将返回一个 resolved 状态的 Promise 对象,Promise 对象的值就是这个返回值。
  • return 另一个 Promise,then 方法将根据这个 Promise 的状态和值创建一个新的 Promise 对象返回。
  • throw 一个同步异常,then方法将返回一个 rejected 状态的 Promise, 值是该异常。

异常

var p1 = new Promise( function(resolve,reject){
  foo.bar();
  resolve( 1 );	  
});

p1.then(
  function(value){
    console.log('p1 then value: ' + value);
  },
  function(err){
    console.log('p1 then err: ' + err);
  }
).then(
  function(value){
    console.log('p1 then then value: '+value);
  },
  function(err){
    console.log('p1 then then err: ' + err);
  }
);

var p2 = new Promise(function(resolve,reject){
  resolve( 2 );	
});

p2.then(
  function(value){
    console.log('p2 then value: ' + value);
    foo.bar();
  }, 
  function(err){
    console.log('p2 then err: ' + err);
  }
).then(
  function(value){
    console.log('p2 then then value: ' + value);
  },
  function(err){
    console.log('p2 then then err: ' + err);
    return 1;
  }
).then(
  function(value){
    console.log('p2 then then then value: ' + value);
  },
  function(err){
    console.log('p2 then then then err: ' + err);
  }
);

输出
p1 then err: ReferenceError: foo is not defined
p2 then value: 2
p1 then then value: undefined
p2 then then err: ReferenceError: foo is not defined
p2 then then then value: 1

Promise 中的异常由 then 参数中第二个回调函数处理,异常信息将作为Promise的值。异常一旦得到处理,then 返回的后续 Promise 对象将恢复正常,并会被 Promise 执行成功的回调函数处理。另外,需要注意p1、p2 多级then的回调函数是交替执行的 ,这正是由Promise then回调的异步性决定的。

resolve vs reject

var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){
  resolve(1);
});
var p4 = new Promise(function(resolve, reject){
  resolve(p1);
});

console.log(p1 === p2); 
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);

p4.then(function(value){
  console.log('p4=' + value);
});

p2.then(function(value){
  console.log('p2=' + value);
})

p1.then(function(value){
  console.log('p1=' + value);
}) 

输出
true
false
false
false
p2=1
p1=1
p4=1

Promise.resolve()可以接收一个值或者是一个 Promise 对象作为参数。当参数是普通值时,它返回一个 resolved 状态的 Promise 对象,对象的值就是这个参数;当参数是一个 Promise 对象时,它直接返回这个 Promise 参数。因此,p1 === p2。但通过 new 的方式创建的 Promise 对象都是一个新的对象,因此后面的三个比较结果都是false。另外,为什么 p4 的 then 最先调用,但在控制台上是最后输出结果的呢?因为 p4 的 resolve 中接收的参数是一个 Promise 对象 p1,resolve 会对 p1 “拆箱”,获取 p1 的状态和值,但这个过程是异步的。

var p1 = new Promise(function(resolve, reject){
  resolve(Promise.resolve('resolve'));
});

var p2 = new Promise(function(resolve, reject){
  resolve(Promise.reject('reject'));
});

var p3 = new Promise(function(resolve, reject){
  reject(Promise.resolve('resolve'));
});

p1.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p2.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p3.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

输出
p3 rejected: [object Promise]
p1 fulfilled: resolve
p2 rejected: reject

Promise 回调函数中的第一个参数 resolve,会对 Promise 执行”拆箱”动作。即当 resolve 的参数是一个 Promise 对象时,resolve 会”拆箱”获取这个 Promise 对象的状态和值,但这个过程是异步的。p1 “拆箱”后,获取到 Promise 对象的状态是 resolved,因此 fulfilled 回调被执行;p2 “拆箱”后,获取到 Promise 对象的状态是 rejected,因此 rejected 回调被执行。但 Promise 回调函数中的第二个参数 reject 不具备”拆箱“的能力,reject 的参数会直接传递给 then 方法中的 rejected 回调。因此,即使p3 reject 接收了一个 resolved 状态的 Promise,then 方法中被调用的依然是 rejected,并且参数就是 reject 接收到的 Promise 对象。

参考连接

八段代码彻底掌握 Promise
Promise 对象