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上提供的示例中,我找不到任何与承诺函数链接的同步函数(返回值而不涉及任何回调的函数)的示例。

任何想法如何将同步操作与异步承诺混合在一起?

我试过了:

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

..仍然不是我想要达到的目标。

4

3 回答 3

13

如果要链接回调,则必须从其中一个回调中返回一个新的 Promise 对象。在你的第一个例子中,你写

.then( delay( 7500 ) )

这意味着您将 promise 对象传递给.then,而不是函数。根据Promise/A+ 提议(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 对象被解析后,才会调用最后一个回调。

于 2013-06-20T12:48:23.363 回答
3

Google 在解决类似问题时将我带到这里(但使用 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);
}
于 2013-12-17T15:57:50.050 回答
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);
  });
于 2015-10-23T09:08:32.747 回答