gpt4 book ai didi

javascript - 我应该如何在JavaScript中“屈服”?

转载 作者:行者123 更新时间:2023-11-29 18:46:40 25 4
gpt4 key购买 nike

我对现代JavaScript(ES8)有点陌生。异步产生的首选方法是什么,即使用await在事件循环的某些将来迭代中继续执行脚本?我看到了以下选项:

async function yield1() {
await Promise.resolve();
console.log("done");
}

async function yield2() {
// setImmediate is non-standard, only Edge and Node have it
await new Promise(done => (setImmediate? setImmediate: setTimeout)(done));
console.log("done");
}

async function yield3() {
await new Promise(done => setTimeout(done));
console.log("done");
}


我应该选择一个还是另一个吗?还是取决于环境(节点,浏览器)?



在评论中被询问有关我要实现的目标的更新。这是一个简单的可观察对象,当其属性更改时会异步触发 propertyChanged事件。这是一个完整的示例,“产量”部分位于 firePropertyChanged内:

const EventEmitter = require('events');

class Model extends EventEmitter {
constructor(data) {
super();
this._data = data;
}

get data() {
return this._data;
}

set data(newValue) {
const oldValue = this._data;
if (oldValue !== newValue) {
this._data = newValue;
this.firePropertyChanged('data', newValue, oldValue);
}
}

async firePropertyChanged(property, newValue, oldValue) {
await Promise.resolve().then(() =>
super.emit('propertyChanged', { target: this, property, newValue, oldValue }));
console.log('all propertyChanged handlers have been called asynchronously');
}
}

async function waitForChange(obj) {
await new Promise(resolve =>
obj.once('propertyChanged', args =>
console.log(`propertyChanged: ${args.property}, ${args.oldValue} -> ${args.newValue}`)));
}

async function test() {
const obj = new Model("old");
var change = waitForChange(obj);
console.log(`before change: ${obj.data}`);
obj.data = "new";
console.log(`after change: ${obj.data}`);
await change;
}

test().catch(e => console.error(e));


如果与节点一起运行,则预期输出应为:

变更前:旧
更改后:新
propertyChanged:数据,旧->新
所有propertyChanged处理程序均已异步调用


输出的顺序很重要,即我不希望在 propertyChanged的setter方法返回给调用方之前调用 data的任何事件处理程序。

最佳答案

好的,我会在您的评论中讨论您的问题的新摘要(您可能应该编辑您的问题,这样说):


  我想以最有效的方式在事件循环的将来迭代中运行一段代码(并让当前方法返回)。没有特别的偏好,但是连续的顺序应该很重要。例如,在我的示例中,如果property1更改了,那么property2更改了,我首先要为property1然后为property2触发propertyChanged(在两种情况下,都与更改了两个属性的代码异步)。


简短的版本是您可以使用下面的几乎任何选项来解决您的问题。在不了解您的特定情况/要求的情况下,我可能会建议您使用setImmediate(),因为如果递归触发它不会饿死事件队列,但是如果process.nextTick()Promise.resolve().then()会在(其他类型的事件之前)触发得更快一些。对您的来电者很重要。

这是每种选择的一些解释-每个选择都可能实现您的目标,但是每个细节都有所不同。

所有这些选项都允许事件循环的当前滴答声结束,然后它们计划在事件循环的将来滴答声中调用回调。它们的确切区别在于何时调用下一个回调,并且根据当前正在处理的事件类型(例如,事件循环在扫描几个不同事件队列的过程中的位置)安排下一个回调时,它们会有所不同。

您可以先阅读本概述文章The Node.js Event Loop, Timers, and process.nextTick()

process.nextTick(cb)

这是安排回调的最快方法。事件循环的当前滴答声完成了执行,然后在node.js事件循环代码查看事件循环中的任何其他事件队列之前,它先查找nextTickQueue中的项目并运行它们。注意,如果您连续递归调用process.nextTick(),则可能会“饿死”事件循环,因为在nextTickQueue为空之前,它不会给其他事件提供运行的机会。这不是一个“公平的”调度程序。

enter image description here

setImmediate(cb)

这计划在事件循环的当前“阶段”完成后运行回调。您可以将事件循环视为遍历许多不同类型的队列。当前正在处理的队列类型为空时,将处理所有未决的setImmediate()回调。

请注意,这与其他类型的事件有何关系,然后取决于调用setImmediate()时正在处理的事件类型。

例如,如果您处于fs.read()的完成回调中,并且调用setImmediate()安排了回调,那么事件循环将首先处理所有其他未决的I / O事件,然后再处理setImmediate()回调。因为只有在事件循环前进到事件队列中的下一个事件类型时,它才被调用,所以您不能使setImmediate()饿死事件循环。即使递归调用setImmediate()仍会循环遍历所有事件。

相对于您计划的setTimeout()如何处理待处理的setImmediate()取决于您调用setImmediate()时处于事件循环的哪个阶段。通常,这超出了代码中应注意的范围。如果像这样的多个异步操作的相对计时很重要,那么编写保证给定序列的代码要安全得多,而不管它们的回调何时启用它们。承诺可以帮助您对此类事情进行排序。

setTimeout(cb,0)

计时器是事件循环的一个阶段。在围绕事件循环查看不同类型的事件队列时,阶段之一是查找时间已过去的所有计时器事件,因此该调用它们的回调了。由于此计时器仅在事件循环处于“计时器阶段”时才运行,因此它们相对于其他类型的事件的触发方式不确定。当计时器准备就绪时,取决于事件循环在其循环中的位置。就我个人而言,除非我试图与其他计时器事件进行同步,否则我通常不使用setTimeout(cb, 0),因为这将保证与其他计时器事件(而非其他类型的事件)的FIFO顺序。

Promise.resolve()。then(cb)

为了达到承诺的详细级别(通常不需要),您必须非常清楚所使用的promise实现是什么以及它如何工作。非本机代码承诺实现将使用其他计时机制之一来调度其.then()处理程序。它们中的任何一个都可以适当地满足Promise规范,因此它们可以变化。

node.js中的本机承诺确实具有特定的实现。就我个人而言,我不知道为什么您应该编写依赖于此特定实现的代码,但是很多人似乎很好奇,因此我将进行解释。

您可以在本文中看到一个很好的图:Promises, nextTicks and setImmediates。本机承诺是使用微任务队列来实现的。本质上,它是另一个队列,如nextTick队列,该队列在nextTickQueue之后但在其他任何队列之前进行处理。因此,排队的.then().catch()处理程序在已调度的nextTick调用之后和之后且在任何其他类型的事件(计时器,I / O完成等)之前立即运行。

enter image description here

非本机Promise实现(例如Bluebird或Q)无法创建在nextTick队列之后处理的新microTasks队列,因此它们使用setImmediate()process.nextTick()

关于javascript - 我应该如何在JavaScript中“屈服”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53337067/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com