typescript 在JavaScript中使用代理的可更新promise

rjee0c15  于 2023-06-07  发布在  TypeScript
关注(0)|答案(1)|浏览(141)

编辑:我更新了这个问题,问题移动了here

可以在此jsfiddle或下面的代码片段中找到POC

目标:

这里的目标是创建或者说模拟一个可以更新的promise,它的生命周期基本上可以重置。
可选地,我希望使用 inversion of control 对promise解析进行更多控制,如下所示:

const ex = {};
  const prom = new Promise<T>((resolve, reject) => {
    ex.resolve = resolve;
    ex.reject = reject;
  });

实现方式:

片段:
您可以键入新值,然后选择是拒绝还是解决promise。拒绝的值将突出显示。
默认情况下,promise被拒绝,值为default

编辑:默认附带.then()回调(代码片段第85行)。因此,一旦你第一次解析或拒绝一个值,你会看到你的值被打印两次**,这是预期的行为。

class UpdatablePromise {
  /**
   * @param prom - the default promise object
   * @param executor - optional ExternalExecutor argument to store executor internally.
   * @returns UpdatablePromise
   */
  constructor(prom, executor) {
    this.value = undefined;
    this.executors = [];
    if (executor) this.executors.push(Object.assign({}, executor));
    /**
     * Call previous resolve/reject functions to fulfil previous promises, if any.
     * helper function, just to reduce code duplication in `update` function.
     */
    const prevResolvers = (type) => {
      return (val) => {
        const flag = type === "reject";
        while (true) {
          const executor = this.executors.shift();
          if (executor) flag ? executor.reject(val) : executor.resolve(val);
          else break;
        }
        return val;
      };
    };
    const handler = {
      get: (target, prop) => {
        // console.log(prop);
        if (prop === "update") {
          // return a function which takes the new promise value and its executor as argument
          return (v, executor) => {
            // console.log('upd', v, this.value);
            this.value = v;
            if (executor) this.executors.push(executor);
            // attach `then` function to the new promise, so that the old promises are fulfilled when the new one does
            // this has no effect on already fulfilled promises.
            v.then(
              (val) => prevResolvers("resolve")(val),
              (val) => prevResolvers("reject")(val)
            );
          };
        } else if (typeof this.value === "undefined") {
          // if value is undefined, i.e. promise was never updated, return default property values
          return typeof target[prop] === "function" ?
            target[prop].bind(target) :
            target[prop];
        }
        // else,  attach functions to new promise
        else if (prop === "then") {
          //   console.log('then', this.value);
          return (onfulfilled, onrejected) =>
            this.value.then(onfulfilled, onrejected);
        } else if (prop === "catch") {
          return (onrejected) => this.value.catch(onrejected);
        } else if (prop === "finally") {
          return (onfinally) => this.value.finally(onfinally);
        } else return target[prop];
      },
    };
    return new Proxy(prom, handler);
  }
}


const input = document.getElementById("input")
const output = document.getElementById("output")
const resBtn = document.getElementById("res")
const rejBtn = document.getElementById("rej")

const ex_1 = {
  resolve: () => void 0,
  reject: () => void 0,
};

const prom_1 = new Promise((resolve, reject) => {
  ex_1.resolve = resolve;
  ex_1.reject = reject;
});

const up = new UpdatablePromise(prom_1, ex_1)

// Await the promise
up.then(_ => print(_), _ => print(_))

// Print the value of promise, highlight if it is rejected
async function print() {
  try {
    const val = await up;

    output.innerHTML += `
${val}`

  } catch (e) {
    output.innerHTML += `
<mark>${e}</mark>`
  }
}

resBtn.addEventListener("click", () => {
  up.update(Promise.resolve(input.value));
  print();
})

rejBtn.addEventListener("click", () => {
  up.update(Promise.reject(input.value));
  print();
})

function consoleTest() {
  const ex = {
    resolve: () => void 0,
    reject: () => void 0,
  };

  const prom = new Promise((resolve, reject) => {
    ex.resolve = resolve;
    ex.reject = reject;
  });

  const up = new UpdatablePromise(prom, ex);

  setTimeout(() => ex.resolve("resolved"), 1000);

  up.then(
    (_) => console.log(_, "res"),
    (_) => console.log(_, "rej")
  );

  setTimeout(async() => {
    up.update(Promise.reject("reject"));
    up.then(
      (_) => console.log(_, "res"),
      (_) => console.log(_, "rej")
    );
  }, 2000);
  setTimeout(async() => {
    up.update(Promise.resolve("res again"));
    up.then(
      (_) => console.log(_, "res"),
      (_) => console.log(_, "rej")
    );
  }, 2500);
}

consoleTest();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <label for="input">Enter a value to update promise with:</label>
  <input id="input" />

  <div>
    <button id="res">
        resolve
      </button>

    <button id="rej">
        reject
      </button>
  </div>
  <pre id="output"></pre>
</body>

</html>

JS Fiddle
ts playground - ts版本(可能不会更新。更喜欢小提琴版本)
下面是打印promise值的部分:

// Print the value of promise, highlight if it is rejected
async function print() {
  try {
    const val = await up;

    output.innerHTML += `
${val}`

  } catch (e) {
    output.innerHTML += `
<mark>${e}</mark>`
  }
}

问题

它 * 似乎 * 工作 * 有时 *,例如,在控制台或在小提琴。
但是当我在service worker中使用它时实际上不起作用,只返回promise的旧值。我用的是工作盒,如果有关系的话。
我还没找到问题所在下面是对我不起作用的实际工作框代码:

// If the refresh Token is resolved, the user is logged in, otherwise not.
class MyStrat extends Strategy {
  async _handle() {
    try {
      await refreshToken;

      return new Response(JSON.stringify({ is_logged_in: true }), {
        status: 200,
        headers: [[CONTENT_TYPE, APPLICATION_JSON]],
      });
    } catch (e) {
      console.log('error', e);
      return new Response(JSON.stringify({ is_logged_in: false }), {
        status: 400,
        headers: [[CONTENT_TYPE, APPLICATION_JSON]],
      });
    }
  }
}

精确问题:

我想知道几件事
1.它真的像上面提到的那样有效吗?(即,它的工作方式与promise完全相同,除了它可以“重置”,并且一旦更新,所有挂起的await s或附加的回调都会使用新的更新值来实现)
1.如果是,那么它在(工作盒的)service worker中不工作的原因是什么?
1.如果没有,那么实际上发生了什么?
1.如果它真的有效,那么是否可以添加一个state成员变量来同步跟踪promise状态?(我试过一次,根本没用)
编辑二:
显然,这似乎也适用于服务工作者。由于其他一些函数,promise被重复更新为存储在数据库中的值。
不过,我还是想得到我其他问题的答案

t9aqgxwy

t9aqgxwy1#

它真的像我期望它在控制台或小提琴中工作一样工作吗?
只有你才能知道它是否符合你的预期。但是如果你说它可以工作,在控制台中,在小提琴中,(根据你的编辑)也在服务工人中,看起来它确实可以工作。
但是,这里没有充分的理由使用Proxy。如果你使用一个 Package 了promise的常规类示例,实现会变得更加清晰:

class UpdatableResource {
  /**
   * @param prom - the default promise object
   * @param resolver - optional resolver function to resolve the promise when updating.
   */
  constructor(prom, resolver) {
    this.value = Promise.resolve(prom);
    this.handlers = [];
    if (resolver) this.handlers.push(resolver);
  }
  /**
   * Call previous resolve/reject functions to fulfil previous promises, if any.
   * helper function, just to reduce code duplication in `update` function.
   */
  _runHandlers(val) {
    while (this.handlers.length) {
      const handler = this.handlers.shift();
      handler(val);
    }
  }
  /* a function which takes the new promise value and its executor as argument */
  update(v, handler) {
    // console.log('upd', v, this.value);
    this.value = Promise.resolve(v);
    // resolve the old promise(s) to the new one, so that the old promises are fulfilled when the new one does
    this._runHandlers(v);
    if (handler) this.handlers.push(handler);
  }
  then(onfulfilled, onrejected) {
    return this.value.then(onfulfilled, onrejected);
  }
  catch(onrejected) {
    return this.value.catch(onrejected);
  }
  finally(onfinally) {
    return this.value.finally(onfinally);
  }
}

是否可以添加一个state成员变量来同步跟踪promise状态?
当然,只要加上

this.state = "pending";
this.value.then(() => {
  this.state = "fulfilled";
}, () => {
  this.state = "rejected";
});

在每次将promise赋值给this.value之后(或者更好的是,将其放入helper方法中)。

相关问题