dx_*_*_dt 78 javascript asynchronous promise
我正在做一些单元测试.测试框架将页面加载到iFrame中,然后针对该页面运行断言.在每个测试开始之前,我创建一个Promise设置iFrame的onload事件来调用resolve(),设置iFrame src,并返回promise.
所以,我可以调用loadUrl(url).then(myFunc),它会在执行任何myFunc操作之前等待页面加载.
我在我的测试中使用这种模式(不只是用于加载URL),主要是为了允许更改DOM(例如模仿单击按钮,等待div隐藏和显示).
这种设计的缺点是我不断编写匿名函数,其中包含几行代码.此外,虽然我有一个解决方法(QUnit assert.async()),但定义promise的测试函数在promise运行之前就完成了.
我想知道是否有任何方法可以从a Promise或等待(块/睡眠)获取值,直到它已解决,类似于.NET IAsyncResult.WaitHandle.WaitOne().我知道JavaScript是单线程的,但我希望这并不意味着函数不能产生.
从本质上讲,有没有办法让以下方法以正确的顺序吐出结果?
function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");
    
    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
};
function getResultFrom(promise) {
  // todo
  return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>jfr*_*d00 62
我想知道是否有任何方法可以从Promise获取值或等待(阻塞/休眠)直到它已解决,类似于.NET的IAsyncResult.WaitHandle.WaitOne().我知道JavaScript是单线程的,但我希望这并不意味着函数不能产生.
浏览器中当前生成的Javascript没有wait()或sleep()允许其他东西运行.所以,你根本无法做你所要求的.相反,它具有异步操作,可以完成它们的操作,然后在完成后调用您(因为您一直在使用promises).
部分原因是因为Javascript的单线程性.如果单个线程正在旋转,那么在旋转线程完成之前,其他任何Javascript都无法执行.ES6引入yield和生成器将允许这样的一些合作技巧,但我们有很多方法可以在大量已安装的浏览器中使用它们(它们可以用于控制JS引擎的一些服务器端开发)正在使用).
仔细管理基于承诺的代码可以控制许多异步操作的执行顺序.
我不确定我是否完全理解您在代码中尝试实现的顺序,但是您可以使用现有kickOff()函数执行此类操作,然后.then()在调用它之后将处理程序附加到它:
function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");
    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
}
kickOff().then(function(result) {
    // use the result here
    $("#output").append(result);
});
这将以保证顺序返回输出 - 如下所示:
start
middle
end
2018年更新(写完这个答案后三年):
如果您或者您的transpile代码或支持ES7等功能的环境中运行你的代码async和await,你现在可以使用await,使您的代码"显示"等待承诺的结果.它仍在以承诺发展.它仍然不会阻止所有Javascript,但它确实允许您以更友好的语法编写顺序操作.
而不是ES6的做事方式:
someFunc().then(someFunc2).then(result => {
    // process result here
}).catch(err => {
    // process error here
});
你可以这样做:
// returns a promise
async function wrapperFunc() {
    try {
        let r1 = await someFunc();
        let r2 = await someFunc2(r1);
        // now process r2
        return someValue;     // this will be the resolved value of the returned promise
    } catch(e) {
        console.log(e);
        throw e;      // let caller know the promise was rejected with this reason
    }
}
wrapperFunc().then(result => {
    // got final result
}).catch(err => {
    // got error
});
Jos*_*das 13
如果您使用ES2016可以用async和await做类似:
(async () => {
  const data = await fetch(url)
  myFunc(data)
}())
如果您使用的是ES2015,则可以使用生成器.如果您不喜欢语法,可以使用async实用程序函数将其抽象出来,如此处所述.
最后,如果您使用的是ES5,您可能希望像Bluebird这样的库能够为您提供更多控制权.
另一种选择是使用 Promise.all 等待一系列 Promise 解决,然后对它们采取行动。
下面的代码显示了如何等待所有 promise 解决,然后在它们都准备好后处理结果(因为这似乎是问题的目标);同样出于说明目的,它显示了执行期间的输出(中间结束前结束)。
function append_output(suffix, value) {
  $("#output_"+suffix).append(value)
}
function kickOff() {
  let start = new Promise((resolve, reject) => {
    append_output("now", "start")
    resolve("start")
  })
  let middle = new Promise((resolve, reject) => {
    setTimeout(() => {
      append_output("now", " middle")
      resolve(" middle")
    }, 1000)
  })
  let end = new Promise((resolve, reject) => {
    append_output("now", " end")
    resolve(" end")
  })
  Promise.all([start, middle, end]).then(results => {
    results.forEach(
      result => append_output("later", result))
  })
}
kickOff()<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated during execution: <div id="output_now"></div>
Updated after all have completed: <div id="output_later"></div>| 归档时间: | 
 | 
| 查看次数: | 172527 次 | 
| 最近记录: |