期约连锁 因为每个Promise
实例的方法(then()
catch()
finally()
)都可以返回一个Promise
实例,新的Promise
实例又有自己的实例方法,这样连缀的方法调用就可以构成所谓的“期约连锁“,也就是平时说的链式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let p1 = new Promise ((resolve, reject ) => { console .log ('p1 executor' ); setTimeout (resolve, 1000 ); }); p1.then (() => new Promise ((resolve, reject ) => { console .log ('p2 executor' ); setTimeout (resolve, 1000 ); })) .then (() => new Promise ((resolve, reject ) => { console .log ('p3 executor' ); setTimeout (resolve, 1000 ); })) .then (() => new Promise ((resolve, reject ) => { console .log ('p4 executor' ); setTimeout (resolve, 1000 ); }));
把生成期约的代码提取到一个工厂函数中,就可以简洁代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function delayedResolved (str ) { return new Promise ((resolve, reject ) => { console .log (str); setTimeout (resolve, 1000 ); }) } delayedResolve ('p1 executor' ).then (() => delayedResolve ('p2 executor' )) .then (() => delayedResolve ('p3 executor' )) .then (() => delayedResolve ('p4 executor' ))
期约连锁很好地解决了回调地狱的问题
Promise图 因为一个期约可以有任意多个处理程序,所以期约连锁可以构建有向非循环图 的结构
这样,每个期约都是图中的一个节点,而使用实例方法添加的处理程序则是有向顶点
因为图中的每个节点都会等待前一个节点落定,所以图的方向就是期约的解决或拒绝顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let A = new Promise ((resolve, reject ) => { console .log ('A' ); resolve (); }); let B = A.then (() => console .log ('B' ));let C = A.then (() => console .log ('C' ));B.then (() => console .log ('D' )); B.then (() => console .log ('E' )); C.then (() => console .log ('F' )); C.then (() => console .log ('G' ));
(重点)Promise合成 (1)Promise.all()
手写原理 | Promise.all
Promise.all()
静态方法创建的期约会在一组期约全部解决之后再解决
Promise.all()
法接收一个可迭代对象,返回一个新Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let p1 = Promise .all ([ Promise .reslove (), Promise .resolve () ]); let p2 = Promise .all ([3 , 4 ]);let p3 = Promise .all ([]);let p4 = Promise .all ();
Promise.all只会在每个包含Promise都解决之后才会解决
1 2 3 4 5 6 7 8 let p = Promise .all ([ Promise .resolve (), new Promise ((resolve, reject ) => setTimeout (resolve, 1000 )) ]); setTimeout (console .log , 0 , p); p.then (() => setTimeout (console .log , 0 , 'all() resolved!' ));
如果至少一个包含的Promise待定,则合成的Promise也会待定
1 2 3 let p1 = Promise .all ([new Promise (() => {})]);setTimeout (console .log , 0 , p1);
如果有一个包含的Promise拒绝,则最终合成的Promise也会拒绝
1 2 3 4 5 6 7 8 9 10 11 12 13 let p1 = Promise .all ([new Promise (() => {})]);setTimeout (console .log , 0 , p1); let p2 = Promise .all ([ Promise .resolve (), Promise .reject (), Promise .resolve () ]); setTimeout (console .log , 0 , p2);
如果所有的Promise都解决,则合成的Promise的解决值就是所有Promise解决值的数组,顺序按照迭代顺序
1 2 3 4 5 6 7 let p = Promise .all ([ Promise .resolve (3 ), Promise .resolve (), Promise .resolve (4 ) ]); p.then ((values ) => setTimeout (console .log , 0 , values));
如果有期约拒绝,则第一个拒绝的期约会将自己的理由作为合成期约的拒绝理由,之后再拒绝的期约不会影响最终期约的拒绝理由,不过,这并不影响所有包含期约正常的拒绝操作,合成的期约会静默处理所有包含期约的拒绝操作
1 2 3 4 5 6 7 8 9 10 let p = Promise .all ([ Promise .reject (3 ), new Promise ((resolve, reject ) => setTimeout (reject, 1000 )) ]); p.catch ((reason ) => setTimeout (console .log , 0 , reason));
(2)Promise.race()
Promise.race()
静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像
Promise.race()
接收一个可迭代数组,返回一个新的Promise
1 2 3 4 5 6 7 8 9 10 11 12 let p1 = Promise .race ([ Promise .resolve (), Promise .resolve () ]); let p2 = Promise .race ([3 , 4 ]);let p3 = Promise .race ([]);let p4 = Promise .race ();
Promise.race()
不会对解决或拒绝的期约区别对待。无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()
就会包装其解决值或拒绝理由并返回新期约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let p1 = Promise .race ([ Promise .resolve (3 ), new Promise ((resolve, reject ) => setTimeout (reject, 1000 )) ]); setTimeout (console .log , 0 , p1); let p2 = Promise .race ([ Promise .reject (4 ), new Promise ((resolve, reject ) => setTimeout (resolve, 1000 )) ]); setTimeout (console .log , 0 , p2); let p3 = Promise .race ([ Promise .resolve (5 ), Promise .resolve (6 ), Promise .resolve (7 ) ]); setTimeout (console .log , 0 , p3);
如果有一个期约拒绝,只要它是第一个落定的,就会成为拒绝合成期约的理由,之后再拒绝的期约不会影响最终期约的拒绝理由,不过,这并不影响所有包含期约正常的拒绝操作,与Promise.all()
类似,合成的期约会静默处理所有包含期约的拒绝操作
1 2 3 4 5 6 7 8 9 10 let p = Promise .race ([ Promise .reject (3 ), new Promise ((resolve, reject ) => setTimeout (reject, 1000 )) ]); p.catch ((reason ) => setTimeout (console .log , 0 , reason));
串行Promise合成
Promise
的一个主要特性是:异步产生值并将其传给处理程序,基于后续期约使用之前期约的返回值来串联期约是期约的基本功能
参考函数合成,我们也可以将Promise
进行合成
1 2 3 4 5 6 7 8 9 10 11 function addTwo (x ) {return x + 2 ;}function addThree (x ) {return x + 3 ;}function addFive (x ) {return x + 5 ;}function addTen (x ) { return Promise .resolve (x) .then (addTwo) .then (addThree) .then (addFive); } addTen (8 ).then (console .log );
1 2 3 4 5 6 7 function addTwo (x ) {return x + 2 ;}function addThree (x ) {return x + 3 ;}function addFive (x ) {return x + 5 ;}function addTen (x ) { return [addTwo, addThree, addFive].reduce ((promise, fn ) => promise.then (fn), Promise .resolve (x)) }
1 2 3 4 5 6 7 8 9 10 11 function addTwo (x ) {return x + 2 ;}function addThree (x ) {return x + 3 ;}function addFive (x ) {return x + 5 ;}function compose (...fns ) { return (x ) => fns.reduce ((promise, fn ) => promise.then (fn), Promise .resolve (x)) } let addTen = compose (addTwo, addThree, addFive);addTen (8 ).then (console .log );
知识补充 手写原理 | Promise.all
手写原理 | Promise.race
手写原理 | Promise.allSelected
参考 Promises/A+