我能在JavaScript Q库中创建同步Promise吗?

15

我希望能做类似以下的事情:

delay( 2500 )
  .then( function () { console.log( "Step 1 done" ) } )
  .then( delay( 7500 ) )
  .then( function () { console.log( "Step 2 done" ) } );

因此,延迟的实现已经在许多情况下进行了演示:

function delay( ms ) {
  var deferred = Q.defer();
  setTimeout( deferred.resolve, ms );
  return deferred.promise;
}

但是,如果我在Node.js中运行上面的代码,我会得到以下输出:

... delay of 2500ms
Step 1 done
Step 2 done
... delay of ~7500ms

与我预期看到的不同:

... delay of 2500ms
Step 1 done
... delay of 7500ms
Step 2 done
https://github.com/kriskowal/q/wiki/Examples-Gallery提供的示例中,我找不到任何将同步函数(返回值而无需回调的函数)与promise函数链接在一起的示例。你有什么想法如何将同步操作与异步promises混合使用? 我已经尝试过:
function synchronousPromise() {
  var deferred = Q.defer();
  console.log( "Synchronous function call" );
  deferred.resolve();
  return deferred.promise;
}

delay( 2500 )
  .then( function(){synchronousPromise()} )
  .then( function(){delay( 7500 )} )
  .then( function(){synchronousPromise()} );

并且这个将会输出:

... delay of 2500ms
Time now is 2013-06-20
Time now is 2013-06-20
... delay of 7500ms

... 这仍不是我试图实现的。


1
在你的第一个例子中,你尝试过使用.then( function() { return delay( 7500 );})而不是.then( delay( 7500 ) )吗? - Felix Kling
@FelixKling 针对第一个和第二个示例都有效!将其作为您的答案,我会接受。 - PP.
现在,你可以实现这一点,而无需指定延迟时间。请查看我的答案 - Remo H. Jansen
3个回答

13
如果您想要链式调用回调函数,您需要从其中一个回调函数中返回一个新的promise对象。在您的第一个例子中,您写下了:
.then( delay( 7500 ) )

这意味着您将一个 Promise 对象传递给了 .then,而不是一个函数。根据 Promise/A+ proposal(Q 遵循的规范),所有非函数参数都必须被忽略。因此,基本上与您仅编写以下内容相同:
delay( 2500 )
  .then( function () { console.log( "Step 1 done" ) } )
  .then( function () { console.log( "Step 2 done" ) } );

相反,传递一个调用delay并返回promise对象的函数:

delay( 2500 )
  .then( function () { console.log( "Step 1 done" ); } ) 
  .then( function () { return delay( 7500 ); } )
  .then( function () { console.log( "Step 2 done" ); } );

现在,只有在第二个回调函数中由delay返回的promise对象被解决后,最后一个回调函数才会被调用。

3

在使用 Kris Kowal 的 Q 库解决类似问题时,我通过谷歌找到了这里。最终我得到了一个非常小的框架,让你可以做到以下几点:

var chain = [
  doNext(delay, 2500),
  doNext(console.log, "Step 1 done"),
  doNext(delay, 7500),
  doNext(console.log, "Step 2 done")
];

doInOrder(chain);

这个框架只有12行代码,很可能可以适用于其他Promise库:

var Q = require('q');

function doNext(fn /* , arguments */){
  var args =  Array.prototype.splice.call(arguments, 1);
  return function(prevRetVal){
    // For my needs I didn't need the results from previous fns
    return fn.apply(null, args)
  }
}

function doInOrder(doNexters, init){
  return doNexters.reduce(Q.when, init);
}

0
如果您使用Babel或TypeScript,可以使用ES6生成器
  'use strict';

  let asyncTask = () =>
    new Promise(resolve => {
      let delay = Math.floor(Math.random() * 1000);

      setTimeout(function () {
        resolve(delay);
      }, delay);
    });

  let makeMeLookSync = fn => {
    let iterator = fn();
    let loop = result => {
      !result.done && result.value.then(res =>
        loop(iterator.next(res)));
    };

    loop(iterator.next());
  };

  makeMeLookSync(function* () {
    let result = yield asyncTask();

    console.log(result);
  });

这是与 https://www.tivix.com/blog/making-promises-in-a-synchronous-manner 相同的代码,那篇文章非常易读。 - myrdd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接