nodeJs:使用promise实现一次性“渠道”

时间:2019-05-13 15:02:51

标签: node.js

我想让一个异步进程等待另一个异步进程向其发送信号。 (一次就够了)

Go中,您可以在两个进程之间创建一个通道。

def foo1(c chan int) {
   // do some stuff
   c <- 1
}

def foo2(c chan int) {
   <-c
   // do stuff later
}

基本上<-c的意思是,“等到有人在c中放入东西”。 c <- 1的意思是“在c中放入1”。 (假设将相同的对象c传递给两个函数)。

我想用node实现类似的东西。我的想法是创建一个承诺,并有一个过程来解决它,另一个过程是await。像这样:

function foo1(promise) {
   // do some stuff
   promise.resolve();
}

function foo2(promise) {
   await promise;
   // do stuff later
}

我该怎么做?

4 个答案:

答案 0 :(得分:1)

您可以使用“ https://www.npmjs.com/package/smem”在异步进程之间发送数据。

// Import.
const SMem = require('smem');

// Create shared memory instance.
const defaultSMem = new SMem();
 
// Get value async.
(async () => {
  console.log(await defaultSMem.get('test-key'));
})();
 
// Set value after 2 seconds.
setTimeout(() => {
  defaultSMem.set('test-key', 'test-value');
}, 2000);

答案 1 :(得分:1)

您可以扩展实现PassThrough流的Transform流,并允许您在objectMode中读写任意数据。从Node v10开始,对Symbol.asyncIterator的支持已添加到streams中,并允许您使用Readable循环或手动使用异步迭代器来消耗for await...of流中的数据next()return()throw()方法,它们返回一个Promise<{ value, done }>

扩展PassThrough

module.exports = class Channel extends require('stream').PassThrough {
  constructor () {
    super({ objectMode : true });
  }

  async * [Symbol.asyncIterator] () {
    const queue = [];
    const onData = data => { queue.push(data); };
    const next = resolve => {
      const onEvent = data => {
        this.off('data', onEvent);
        this.off('end', onEvent);
        resolve();
      };

      this.on('data', onEvent);
      this.on('end', onEvent);
    };

    this.on('data', onData);

    try {
      // internals of Readable; indicates whether end event was emitted
      while (!this._readableState.ended || queue.length > 0) {
        if (queue.length > 0) yield queue.shift();
        else await new Promise(next);
      }
    } finally {
      // unsubscribed in finally in case yield returns or throws
      this.off('data', onData);
    }
  }
};

覆盖[Symbol.asyncIterator]方法是可选的,因为它已经在Readable流中实现。但是,以上实现允许多个并发接收器使用来自Channel相同实例的相同数据,而Readable上存在的默认实现不允许并发接收器使用相同数据。

用法:

const Channel = require('./channel');
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

// source
async function foo1 (c) {
  for (let i = 0; i < 10; i++) {
    await delay(500);
    console.log('foo1', i);
    c.write(i);
  }
  await delay(500);
  c.end();
}
// automatic
async function foo2 (c) {
  for await (const i of c) {
    console.log('foo2', i);
  }
}
// manual
async function foo3 (c) {
  const asyncIterator = c[Symbol.asyncIterator]();
  for (let data; !(data = await asyncIterator.next()).done; ) {
    console.log('foo3', data.value);
  }
}

const channel = new Channel();
foo1(channel);
// both can consume concurrently
foo2(channel);
foo3(channel);

Try it online!


如果您不关心可重用性,那么扩展Promise会更容易

class PromiseCompletionSource extends Promise {
  static [Symbol.species] = Promise;

  constructor () {
    const completions = {};

    super(
      (resolve, reject) => Object.assign(completions, { resolve, reject })
    );

    this.resolve = completions.resolve;
    this.reject = completions.reject;
  }
}

function foo1 (c) {
  c.resolve(1);
  console.log('foo1');
}

async function foo2 (c) {
  console.log('foo2', await c);
}

function foo3 (c) {
  return c.then(i => {
    console.log('foo3', i);
  });
}

const channel = new PromiseCompletionSource();
foo1(channel);
foo2(channel);
foo3(channel);

首先打印foo3的原因是因为await c实际上是Promise.resolve(c).then(...)的语法糖,而不仅仅是c.then(...),所以foo3()将解析值延迟了比foo2()少一分。

答案 2 :(得分:0)

我认为完成此任务的最简单方法是使用EventEmitter

这在Node 11.13(once函数在该版本中)中起作用:

'use strict'

const { EventEmitter, once } = require('events')

const sharedEventEmitterChannel = new EventEmitter()

async function foo1 () {
  console.log('do some stuff')
  await heavyTask()
  sharedEventEmitterChannel.emit('myevent', 1)
  console.log('emitted')
}

async function foo2 () {
  try {
    const num = await once(sharedEventEmitterChannel, 'myevent')
    console.log('do stuff later', num)
  } catch (err) {
    console.log(err)
  }
}

foo2()
foo1()

function heavyTask () {
  return new Promise(resolve => setTimeout(resolve, 1000))
}

对于早期版本的节点:

'use strict'

const { EventEmitter } = require('events')

const sharedEventEmitterChannel = new EventEmitter()

async function foo1 () {
  console.log('do some stuff')
  await heavyTask()
  sharedEventEmitterChannel.emit('myevent', 1)
  console.log('emitted')
}

async function foo2 () {
  sharedEventEmitterChannel.once('myevent', (num) => {
    console.log('do stuff later', num)
  })
}

foo2()
foo1()

function heavyTask () {
  return new Promise(resolve => setTimeout(resolve, 1000))
}

答案 3 :(得分:0)

这是我最后找到的解决方案:

async function foo1(resolveFn) {
  ...
  resolveFn();
  ...
}

async function foo2(promise) {
  ...
  await promise;
  ...
}

const promise = new Promise(resolve, reject) => {
  foo1(resolve);
});

await foo2(promise)