Обещание очереди обратного вызова с приоритетом для некоторых обратных вызовов - PullRequest
0 голосов
/ 05 октября 2019

Мой код должен выполнять множество асинхронных действий одновременно и обрабатывать их обещания определенным последовательным способом.

Что я подразумеваю под "определенным последовательным способом" ==> Предположим, вы запускаете обещания promise1, promise2, promise3 в таком порядке, и это обещание3 на самом деле разрешается первым, а обещание2 - вторым, тогда я хочу последовательно обработать promise3, promise2 и promise1 в этом порядке

Давайте рассмотрим асинхронную функцию timeout, которая истекает через X секунд, и fetchMyItem1, fetchMyItem2, которые возвращают обещания, которые при выполнении должны выполнять другой код в зависимости от того, разрешено timeout или нет.

Для конкретного сценария представьте, что у стойки есть клиент, ожидающий доставки товара, и либо клиент остается, и мы можем обслуживать его непосредственно у стойки, принося по одной позиции за раз, либо клиентуходит (timeout), и мы должны попросить официанта прийти, чтобы, когда предметы прибыли, он / она мог принести предметы ему. Обратите внимание, что даже когда один элемент доставляется, другие элементы все еще должны быть доставлены (обещания ожидают выполнения) и могут даже прибыть (быть выполненными), пока клиент получает один элемент или сервер прибывает.

Вот некоторый код, чтобы начать с

const allItemsToBeDeliveredPromises = [fetchMyItem1(), fetchMyItem2(), ...]
const customerLeavesCounterPromise = timeout()

const waiter = undefined

const allPromisesToBeFulfilled = [...allItemsToBeDeliveredPromises, customerLeavesCounterPromise]

// LOOP
const itemDeliveredOrVisitorLeft = await Promise.all(allPromisesToBeFulfilled)

if hasCustomerLeft(itemDeliveredOrCustomerLeft) {
  // hasCustomerLeft allows us to detect if the promise that resolved first is `customerLeavesCounterPromise` or not
  waiter = await callWaiter()
} else {
  // An item has arrived
  if (waiter) {
    deliverItemViaWaiter(itemDeliveredOrVisitorLeft)
  } else {
    deliverItemAtCounter(itemDeliveredOrVisitorLeft)
  }
}
// remove itemDeliveredOrCustomerLeft from allPromisesToBeFulfilled

// END loop

Я не уверен, как реализовать цикл для этого сценария. Обещания должны накапливаться в очереди по мере их разрешения, но для определенного обещания в очереди есть приоритет (обещание тайм-аута должно выполняться как можно скорее, когда оно прибывает, но после завершения обработки текущего обещания, если выполнение обещания уже выполнено. обрабатывается)

Ответы [ 3 ]

1 голос
/ 05 октября 2019

Мой код должен выполнять множество асинхронных действий одновременно и обрабатывать их обещания определенным последовательным способом.

Вы можете использовать потоки для потребления обещаний, так как потоки по сути являются очередямикоторый обрабатывает одно сообщение за раз.

Идея (то есть не проверенная):

import { Readable, Writable } from 'stream';
let customerHasLeft = false;
/*const customerLeavesCounterPromise = */timeout() // your code...
.then(() => { customerHasLeft = true; }); // ... made boolean
// let's push all our promises in a readable stream
// (they are supposedly sorted in the array)
const input = new Readable({
  objectMode: true,
  read: function () { // don't use arrow function: we need `this`
    const allItemsToBeDeliveredPromises = [fetchMyItem1(), fetchMyItem2(), ...]; // your code
    // put everything, in the same order, in the output queue
    allItemsToBeDeliveredPromises.forEach(p => this.push(p));
    this.push(null); // terminate the stream after all these
  }
});
// let's declare the logic to process each promise
// (depending on `timeout()` being done)
const consumer = new Writable({
  write: async function (promise, uselessEncoding, callback) {
    try {
      const order = await promise; // wait for the current promise to be completed
    } catch (e) {
      /* delivery error, do something cool like a $5 coupon */
      return callback(e); // or return callback() without e if you don't want to crash the pipe
    }
    if (customerHasLeft) { /* find a waiter (you can still `await`) and deliver `order` */ }
    else { /* deliver `order` at the counter */ }
    callback(); // tell the underlying queue we can process the next promise now
  }
});
// launch the whole pipe
input.pipe(consumer);
// you can add listeners on all events you'd like:
// 'error', 'close', 'data', whatever...

РЕДАКТИРОВАТЬ: на самом деле мы хотим обрабатывать обещания по мере их разрешения, но последовательно (т.е. один пост-процесс для всех обещаний)

let customerHasLeft = false;
timeout() // your code...
.then(() => { customerHasLeft = true; }); // ... made boolean
const allItemsToBeDeliveredPromises = [fetchMyItem1(), fetchMyItem2(), ...];
const postProcessChain = Promise.resolve(); // start with a promise ready to be thened
// add a next step to each promise so that as soon as one resolves, it registers
//  as a next step to the post-process chain
allItemsToBeDeliveredPromises.forEach(p => p.then(order => postProcessChain.then(async () => {
  // do something potentially async with the resulting order, like this:
  if (customerHasLeft) { /* find a waiter (you can still `await`) and deliver `order` */ }
  else { /* deliver `order` at the counter */ }
})));
1 голос
/ 05 октября 2019

один из них должен иметь такой эффект, что всякий раз, когда он выполняется, он должен изменять обработку других выполненных обещаний, которые еще не были обработаны или еще не обрабатываются.

Вы запускаете их все вместе? Что-то вроде:

promise1 = asyncFunc1()
promise2 = asyncFunc2()
promise3 = asyncFunc3()

Promise.all([promise1, promise2, promise3]).then(//sometthing)

Если да, это невозможно сделать, если функция обещания2 и функция обещания не ожидают события, блокировки или чего-то, что обрабатывается функцией обещания1.

Если это так, лучше организовать такие обещания, как:

asyncFunc1()
    .then(() => asyncFunc2(paramIfOk))
    .catch(() => asyncFunc2(paramIfFail))

В вашем примере:

const allItemsToBeDelivered = [myPromise1(), myPromise2(), ...]

myPromise1 () код должен ждать, пока элемент будетдоставлено и проверить, не ждет ли его кто-нибудь. Это проблема проектирования модели / кода, а не обещания.

Другой способ сделать это - рассмотреть некоторую логику, управляемую событиями: у сущности Customer есть прослушиватель для доставленного события, которое будет вызвано waitItemDelivered ()обещание непосредственно перед разрешением.

РЕДАКТИРОВАТЬ: в соответствии с запросом здесь немного больше подробностей о решении, управляемом событиями.

Краткий ответ: оно сильно зависит от вашего программного обеспечения.

Это уже что-то выпущено и запущено в производство? Будьте осторожны с такими изменениями. Если вы разрабатываете услугу, у вас еще есть время принять во внимание некоторые логические изменения. Решение, которое не меняет в корне то, как работает, но использует события, не так уж и хорошо, шаблоны смешивания никогда не окупаются в долгосрочной перспективе.

Пример:

const events = require('events');

class Customer() {

    constructor(shop) {
        this.emitter = new events.EventEmitter()

        this.shopEventListener = shop.emitter
        this.setupEventLinstening() // for keeping things clean in the constructor

        // other properties here
    }

    setupEventLinstening() {
        this.shopEventListener.on('product-delivered', (eventData) => {
            // some logic
        })
    }

    buyProduct() {
        // some logic

        this.emitter.emit('waiting-delivery', eventData)
    }
}

class Shop() {

    constructor(shop) {
        this.emitter = new events.EventEmitter()

        // other properties here
    }

    addCustomer(customer) {
        customer.emitter.on('waiting-delivery', (eventData) => {
            // some logic

            self.waitDelivery().then(() => self.emitter.emit('product-delivered'))
        })
    }

    waitDelivery() {
        return new Promise((resolve, reject) => {
            // some logic

            resolve()
        })
    }
}

// setup
const shop = new Shop()
const customer = new Customer(shop)
shop.addCustomer(customer)

Это новыйспособ увидеть логику, но подобный подход можно использовать внутри обещания:

const waitDelivery = () => new Promise((resolve, reject) => {
    logisticWaitDelivery().then(() => {
        someEmitter.emit('item-delivered')
        resolve()
    })
}

const customerPromise = () => new Promise((resolve, reject) => {
    someListener.on('item-delivered', () => {
        resolve()
    })
}

promiseAll([waitDelivery, customerPromise])
0 голосов
/ 05 октября 2019

Сделать 2 очереди, вторая начинается после первой:

var highPriority=[ph1,ph2,...]//promises that have to be executed first
var lowPriority=[pl1,pl2,...]//promises that have to wait the high priority promises
Promise.all(highPriority).then(()=>{
    Promise.all(lowPriority)
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...