如果必须创建一个临时变量 p2(或 p3 等),还是有一点麻烦的。谢天谢地,我们很 容易把这些链接到一起:
var p = Promise.resolve( 21 );
p
.then( function(v){
console.log( v ); // 21
// 用值42完成连接的promise
return v * 2; })
// 这里是链接的promise .then( function(v){
直任意扩展下去。只要保持把先前的 then(…) 连到自动创建的每一个 Promise 即可。 但这里还漏掉了一些东西。如果需要步骤 2 等待步骤 1 异步来完成一些事情怎么办?我们
使用了立即返回 return 语句,这会立即完成链接的 promise。
使 Promise 序列真正能够在每一步有异步能力的关键是,回忆一下当传递给 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
现在第一个 then(..) 就是异步序列中的第一步,第二个 then(..) 就是第二步。这可以一
console.log( v );
} );
// 用值42填充
resolve( v * 2 );
} );
})
.then( function(v){
console.log( v );
} );
// 42
虽然我们把 42 封装到了返回的 promise 中,但它仍然会被展开并最终成为链接的 promise 的决议,因此第二个 then(…) 得到的仍然是 42。如果我们向封装的 promise 引入异步,一 切都仍然会同样工作:
var p = Promise.resolve( 21 p.then( function(v){
console.log( v ); // 21
Promise | 199
// 创建一个promise并返回
return new Promise( function(resolve,reject){
// 引入异步! setTimeout( function(){
// 用值42填充
resolve( v * 2 );
}, 100 );
} );
})
.then( function(v){
// 在前一步中的100ms延迟之后运行
console.log( v ); // 42
} );
这种强大实在不可思议!现在我们可以构建这样一个序列:不管我们想要多少个异步步 骤,每一步都能够根据需要等待下一步(或者不等!)。
当然,在这些例子中,一步步传递的值是可选的。如果不显式返回一个值,就会隐式返回 undefined,并且这些 promise 仍然会以同样的方式链接在一起。这样,每个 Promise 的决 议就成了继续下一个步骤的信号。
为了进一步阐释链接,让我们把延迟 Promise 创建(没有决议消息)过程一般化到一个工 具中,以便在多个步骤中复用:
function delay(time) {
return new Promise( function(resolve,reject){
setTimeout( resolve, time );
} );
}
delay( 100 ) // 步骤1 .then( function STEP2(){
console.log( "step 2 (after 100ms)" );
return delay( 200 ); })
.then( function STEP3(){
console.log( "step 3 (after another 200ms)" );
})
.then( function STEP4(){
console.log( "step 4 (next Job)" );
return delay( 50 ); })
.then( function STEP5(){
console.log( "step 5 (after another 50ms)" );
}) ...
调用 delay(200) 创建了一个将在 200ms 后完成的 promise,然后我们从第一个 then(…) 完成 回调中返回这个 promise,这会导致第二个 then(…) 的 promise 等待这个 200ms 的 promise。
如前所述,严格地说,这个交互过程中有两个 promise:200ms 延迟 promise, 和第二个 then(…) 链接到的那个链接 promise。但是你可能已经发现了,在 脑海中把这两个 promise 合二为一之后更好理解,因为 Promise 机制已经自 动为你把它们的状态合并在了一起。这样一来,可以把return delay(200) 看作是创建了一个 promise,并用其替换了前面返回的链接 promise。
但说实话,没有消息传递的延迟序列对于 Promise 流程控制来说并不是一个很有用的示例。 我们来考虑如下这样一个更实际的场景。
这里不用定时器,而是构造 Ajax 请求:
// 假定工具ajax( {url}, {callback} )存在
// Promise-aware ajax
function request(url) {
return new Promise( function(resolve,reject){
// ajax(..)回调应该是我们这个promise的resolve(..)函数 ajax( url, resolve );
} ); }
我们首先定义一个工具 request(…),用来构造一个表示 ajax(…) 调用完成的 promise:
request( "http://some.url.1/" )
.then( function(response1){
return request( "http://some.url.2/?v=" + response1 ); })
.then( function(response2){
console.log( response2 );
} );
化”或者其他类似的名称。我们稍后会再介绍这种技术。
利用返回 Promise 的 request(…),我们通过使用第一个 URL 调用它来创建链接中的第一
步,并且把返回的 promise 与第一个 then(…) 链接起来。
response1 一返回,我们就使用这个值构造第二个 URL,并发出第二个 request(…) 调用。 第二个 request(…) 的 promise 返回,以便异步流控制中的第三步等待这个 Ajax 调用完成。 最后,response2 一返回,我们就立即打出结果。