稍后解决承诺

Posted

技术标签:

【中文标题】稍后解决承诺【英文标题】:Resolve promise at a later time 【发布时间】:2016-04-05 01:05:42 【问题描述】:

我想构造一个 Promise,但推迟解决。下面的代码创建了一个承诺,但它会立即解决。如何控制 promise 何时被评估?

var p = new Promise((resolve, reject) => 
        resolve(1);
    )
    .then((p1) => 
        console.log(p1 + 1);
    );

更新:澄清一下,希望将 Promise 的声明与其执行分开的原因是根据一些参数动态添加 then 回调。

【问题讨论】:

您需要决定 Promise 如何以及为什么会得到解决:没有一般情况。实际上很少需要直接执行此操作,因为大多数异步都已经返回一个 Promise,或者至少做了一些您可以承诺的事情。通常感觉需要直接做这件事本身就表明代码中的某些内容需要重构。 【参考方案1】:

您可以将resolvereject 传递给您想要使用的任何异步函数。这样的函数可以在完成工作时调用它。这是一个可在 Node.js 中运行的示例。如果你运行它,它将在你的当前目录中执行ls -lexecSomething 函数只接受回调,promiseToExec 函数将 resolve, reject 回调传递给 execSomething,而不是立即调用其中任何一个。

const childProcess = require("child_process");

function execSomething(command, options, onSuccess, onError) 
  childProcess.exec(command, options, (err, stdout, stderr) => 
    if (err) 
      onError(err);
    
    onSuccess(stdout, stderr);
  );


function promiseToExec(command, options) 
  return new Promise((resolve, reject) => 
      execSomething(command, options, resolve, reject);
  );


promiseToExec("ls -l").then(console.log.bind(console));

Kazlauskis 建议这样做:

var resolve;
var promise = new Promise(function(fulfill) 
  resolve = fulfill;
);

不要这样做!

当您传递给new Promise 的回调中发生异常时,promise 的规范是这样的,异常将自动转换为promise 拒绝。因此,如果在回调中 throw Error... 发生任何事情,您将获得自动转换。

如果您保存 resolve 回调并将您的逻辑移到您传递给 new Promise 的回调之外,那么您不会获得此自动转换。在回调之外抛出的异常只会向上传递堆栈而不会转换为拒绝承诺。这很糟糕,因为它要求您的函数的用户使用.catch 来捕获被拒绝的承诺 try...catch 用于抛出的异常。这是一种糟糕的设计实践。

下面是说明问题的代码:

// This is how things should be done.
function makeGoodPromise(num) 
  return new Promise((resolve) => 
    if (num < 0) 
      throw new Error("negative num");
    
    resolve(num);
  );


// This is a bad approach because it will sometimes result in synchronous
// exceptions.
function makeBadPromise(num) 
  let resolve;
  const p = new Promise((fullfil) => 
    resolve = fullfil;
  );

  if (num < 0) 
    throw new Error("negative num");
  
  resolve(num);

  return p;


// Shoring up the bad approach with a try... catch clause. This illustrates what
// you need to do convert the exception into a rejection. However, why deal with the
// additional scaffolding when you can just take the simpler approach of not
// leaking the callbacks??
function makeBadPromise2(num) 
  let resolve, reject;
  const p = new Promise((fullfil, deny) => 
    resolve = fullfil;
    reject = deny;
  );

  try 
    if (num < 0) 
      throw new Error("negative num");
    
    resolve(num);
  
  catch (e) 
    reject(e);
  

  return p;



makeGoodPromise(-1).catch(() => console.log("caught the good one!"));

try  
  makeBadPromise(-1).catch(() => console.log("caught the bad one!"));

catch(e) 
  console.log("Oops! Synchronous exception: ", e);


makeBadPromise2(-1).catch(() => console.log("caught the bad2 one!"));

当我在 Node 中执行它时,这是输出:

Oops! Synchronous exception:  Error: negative num
    at makeBadPromise (/tmp/t12/test2.js:17:11)
    at Object.<anonymous> (/tmp/t12/test2.js:48:3)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
caught the good one!
caught the bad2 one!

【讨论】:

【参考方案2】:

不完全确定你在问什么——下面的代码演示了 promise 的构造和调用 then 以相同的顺序发生,但可能在不同的时间执行。更改wait1wait2 的值,看看输出有何不同,但无论时间如何,代码都能正常工作。

我认为由你来实现承诺代码,以便它等待你想要等待的任何条件。在示例中,它是一个简单的setTimeout,但可以想见你可以做任何事情来推迟执行。

您可能需要使用 Chrome 在浏览器中查看这些结果:

var wait1 = 2000;
var wait2 = 1000;

function log(...str) 
  var li = document.createElement('li');
  str.forEach((s) => 
    li.appendChild(document.createTextNode(s));
  );
  document.getElementById("log").appendChild(li);


var p = new Promise((resolve, reject) => 
  setTimeout(() => 
    log("Resolving promise!");
    resolve(1);
  , wait1);
);

log("Promise created!");

setTimeout(() => 
  log("Calling 'then'");
  p.then((p1) => 
    log("Value:", p1 + 1);
  );
, wait2);
&lt;ol id="log" /&gt;

【讨论】:

【参考方案3】:

编辑:正如 Luis 在另一个答案中指出的那样,如果抛出异常,它不会被 Promise 构造函数捕获,而是将此处理留给外部范围。在某些情况下这可能是可取的,但我也建议使用构造函数并将解析器函数作为回调传递。更多信息在这里:Resolve javascript Promise outside function scope

你可以:

var resolve;
var promise = new Promise(function(fulfill) 
  resolve = fulfill;
);

// now you can resolve the promise whenever you want 
promise.then(function() 
  console.log('done!');
);

resolve();

【讨论】:

以上是关于稍后解决承诺的主要内容,如果未能解决你的问题,请参考以下文章

如何导出已解决的承诺并再次导入

Mongoose - 删除使用承诺找到的所有文档

Axios 承诺解决/待处理承诺

从父承诺中解决一系列承诺

并行解决承诺

承诺按顺序运行嵌套承诺并在第一次拒绝时解决