浅谈Promise
Promise
主要用于更好地实现javascript中的异步编程。
首先说一下基本的异步编程中存在的问题。
setTimeout
把回调函数放在事件循环中,如果此时事件循环队列中已有任务,回调需要等待,通常没有抢占,因此setTimeout
精准度不高,一般会在设定的时间之后才触发回调。
回调的问题
- 控制反转,导致信任链断裂。
- 顺序性无法保证(Zalgo)
Promise
-
好处
- 解决回调地狱----链式调用
- 控制反转问题----回调只调用一次
-
规范
- 三种状态:Pending、fulfilled、rejected
- 一旦决议,状态保持不变
- 必须实现then方法(resolve, reject),否则Promise就失去了意义。注意:如果
then
里面没有实现reject
则会进入catch
块 - 接受和拒绝的回调,都只接受一天参数,后面的参数会被自动忽略,如需传递多个参数,可以封装为数组或对象
-
使用
revealing constructor: 直接通过传入函数实例化Promise对象new Promise(function(resolve, reject){})
var p1 = new Promise(function(resolve, reject){ var timeOut = Math.random() * 2; console.log("Promise test1!"); setTimeout(function(){ if (timeOut > 1) { console.log("call resolve"); resolve('200 OK'); }else { console.log("call reject"); reject('timeout in ' + timeOut + ' seconds'); } }, 1000 * timeOut); }).then( function(result){ console.log(result); }, function(err){ console.log(err); } ) /*运行结果1: Promise test1! call resolve 200 OK 运行结果2: Promise test1! call reject timeout in 0.9950313377246083 seconds */
promise函数:以
Promise
作为返回值的函数// define function function promise_test(){ return new Promise(function(resolve, reject){ var timeOut = Math.random() * 2; console.log("Promise test form test function!"); setTimeout(function(){ if (timeOut > 1) { console.log("call resolve"); resolve('200 OK'); }else { console.log("call reject"); reject('timeout in ' + timeOut + ' seconds'); } }, 1000 * timeOut); }); } // call function promise_test() .then( function(result){ console.log(result); }, function(err){ console.log(err); } ) /* 结果1: Promise test form test function! call resolve 200 OK 结果2: Promise test form test function! call reject timeout in 0.6580752245539725 seconds */
-
链式调用:
then
返回的仍然是一个promise
对象,所以可以继续调用then
promise_test() .then( function(result){ console.log("then_0:"+result); return "data from then_0"; }, function(err){ return "err from then_0"; } ).then( function(result){ console.log("then_1:"+result); return "data from then_1"; }, function(err){ return "err from then_1"; } ).then( function(result){ console.log("then_2:"+result); return "data from then_2" }, function(err){ return "err from then_2"; } ).then( function(result){ console.log("then_3:"+result); }, function(err){ return "err from then_3"; } ).catch(function(reason){ console.log(reason); }); /* 结果1: call resolve then_0:200 OK then_1:data from then_0 then_2:data from then_1 then_3:data from then_2 结果2: call reject then_1:err from then_0 then_2:data from then_1 then_3:data from then_2 */
当然,你也可以不用在所有的
then
中实现reject
,只需要在最后增加一个catch
捕获异常即可。比如:promise_test().then(function(result){ console.log(result); }).then(function(result){ console.log("then_1:"+result); return "data from then_1" }).then(function(result){ console.log("then_2:"+result); return "data from then_2" }).then(function(result){ console.log("then_3:"+result); }).catch(function(reason){ console.log(reason); }); /* 结果1: Promise test form test function! call resolve 200 OK then_2:data from then_1 then_3:data from then_2 结果2: Promise test form test function! call reject timeout in 0.9641669406602742 seconds
说明:在这里的链式Promise中,状态被拒绝,由于每个
then
中都没有定义reject,因此直接进入到最后的catch块中(如果在中间某个then
中定义reject
,仍然能进入reject的逻辑) -
并行
Promise.all
(与门):promise
数组中所有的promise
都被接受才被接受,只要有任何一个promise
被拒绝就被拒绝。/*例子中每一个promise都会正常返回,所以也没有使用catch*/ function getRandomNum(num){ return new Promise(function(resolve, reject){ var timeOut = Math.random() * 2; setTimeout(function(){ resolve(num * timeOut); }, 1000 * timeOut); }); } Promise.all([getRandomNum(1), getRandomNum(2), getRandomNum(3)]) .then(function([num1, num2, num3]){ // 函数接收一个数组参数,表示几个promise返回的数据 console.log("num1:" , num1); console.log("num2:" , num2); console.log("num3:" , num3); console.log("num1 + num2 + num3 :", num1 + num2 + num3); }); /* num1: 0.4254007834181297 num2: 0.10943891453033672 num3: 0.34640854515410746 num1 + num2 + num3 : 0.8812482431025739 */
-
静态
Promise.race
(或门):promise
数组里第一个被接受或者被拒绝的promise
决定最终的promise
状态function getRandomNum(num){ return new Promise(function(resolve, reject){ var timeOut = Math.random() * 2; setTimeout(function(){ // 参数类型校验 if ("number" === typeof num) { console.log("random num " + num + ":" + num * timeOut); resolve(num * timeOut); }else { reject("TypeError!"); } }, 1000 * timeOut); }); } // 传入"1"会被拒绝 Promise.race([getRandomNum("1"), getRandomNum(2)]) .then(function(result){ console.log("result:" , result); }).catch(function(reason){ console.log(reason); }); /* 结果1:第一个promise先被拒绝 TypeError! random num 2:1.21016497709787 结果2: 第二个promise先被接受,第一个promise虽然被拒绝,但是无法进入catch random num 2:3.723485222889881 result: 3.723485222889881 */
这里再强调一下
Promise
一旦决议,状态不可变。所以上述竞态例子中,第一个决议的Promise
决定了数据的决议结果。