비동기 캡슐화 개선: 반환 값으로 비동기 호출 처리 - Border City Inn

hzc
풀어 주다: 2020-06-23 10:07:23
앞으로
2900명이 탐색했습니다.
지난 몇 개의 기사는 위챗 미니 프로그램 개발과 관련이 있어서 누군가 "미니 프로그램을 이해하지 못하는데 다른 것을 써주실 수 있나요?"라고 물으셨습니다.

사실, "작은 프로그램" 문제에 너무 많은 관심을 기울일 필요는 없습니다. 왜냐하면 "작은 프로그램"은 기사에 나오는 개발 시나리오일 뿐이기 때문입니다. 우리가 실제로 해결하는 문제는 작은 프로그램에서만 발생하는 것이 아닙니다. 문제를 해결하는 방법은 작은 프로그램과 전혀 다릅니다.

1. 문제

프록시로 캡슐화된 WeChat 애플릿의 비동기 호출에 문제가 남아 있습니다.

wx.request()와 같은 상황에는 반환 값이 있습니다. 그것을 캡슐화하는 방법? wx.request()  这种原本就有返回值的情况,该如何封装呢?

如果需要在请求的过程中取消请求,就会用到 wx.request() 的返回值:

const requestTask = wx.request(...);
if (...) {
    // 因为某些原因需要取消这次请求
    requestTask.abort();
}
로그인 후 복사

封装过后的 awx.request() 会返回一个 Promise 对象,跟 wx.request() 原来的返回值毫无关系。如果想要能够取消请求,就必须将 wx.request() 原来的返回值带出来,应该怎么办?

function wxPromisify(fn) {
    return async function (args) {
        return new Promise((resolve, reject) => {
            const originalResult = fn({
//          ^^^^^^^^^^^^^^^^^^^^^^^
//          怎么把 originalResult 带出去?
                ...(args || {}),
                success: res => resolve(res),
                fail: err => reject(err)
            });
        });
    };
}
로그인 후 복사

2. 可选方案

也不卖关子了,这里有几个方案可选:

  1. 返回对象或数组,解构后使用。比如返回 { promise, originalResult}[promise, originalResult]
  2. 通过一个“容器”参数将返回值带出来,比如 awx.request(params, outBox = {}),在处理时为 outBox 赋值:outBox.originalResult
  3. JS 是动态类型,可以直接修改 Promise 对象,为其附加属性:promise.originalResult = ...

从使用者的角度来考虑,多数时候是不需要原返回值的,这时候是肯定是希望 await awx.request(),而不是先解构再 await(或 then()),所以,第 1 种方法不可选。

第 2 种方法可行,不需要原返回值的时候,直接使用即可。但是需要原返回值的时候,稍嫌麻烦,需要先产生一个容器对象传入。

第 3 种方法使用起来应该是最“无感”的。无论如何,原值随 Promise 对象带出来了,用或是不用,请便!

现在我们来实现第 3 种方法,改造 wxPromisify()

3. 失败的尝试

一开始想得很简单,原来直接 return new Promise(),现在加个临时变量应该就可以吧:

function wxPromisify(fn) {
    return async function (args) {
        const promise = new Promise((resolve, reject) => {
//      ^^^^^^^^^^^^^^^^
            promise.originalResult = fn({
//          ^^^^^^^^^^^^^^^^^^^^^^^^^
                ...(args || {}),
                success: res => resolve(res),
                fail: err => reject(err)
            });
        });
        
        return promise;
//      ^^^^^^^^^^^^^^^
    };
}
로그인 후 복사

然后得到一个错误:

TypeError: Cannot set property 'originalResult' of undefined
로그인 후 복사

这个错很好理解,也很容易改……不过确实也很容易犯!

本来是认为 promise 是个局部变量,可以直接访问,所以在其子作用域中使用是没问题。但是这里忽略了这个子作用域是在构造函数中。来大概分析一下:

new Promise() 需要一个函数(假设叫 factory)作为参数,但是这个 factory 执行的时机是什么?注意到 new Promise() 产生 Promise 实例之后,我们再没有主动调用这个实例的任何方法,所以可以断定,factory 是在构造的过程中执行的。换句话说,这时候 Promise 实例还没产生呢,promise 引用的是 undefined

4. 成功的尝试

既然已经知道问题所在,我们接着分析。

构造 Promise 实例的过程中调用了 factory,而 factory 的在函数体中直接执行了 fn,可以立即拿到 fn 的返回值,所以这个 Promise 实例构造完成之后,是可以拿到原返回值的。

现在来修改一下代码:

function wxPromisify(fn) {
    return async function (args) {
        let originalResult;
//      ^^^^^^^^^^^^^^^^^^^
        const promise = new Promise((resolve, reject) => {
            originalResult = fn({
//          ^^^^^^^^^^^^^^
                ...(args || {}),
                success: res => resolve(res),
                fail: err => reject(err)
            });
        });

        promise.originalResult = originalResult;
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        return promise;
    };
}
로그인 후 복사

我们需要在 new Promise() 之后对 promise.originalResult 赋值,而这个“值”产生于 new Promise() 的过程中,那么再加个局部变量 originalResult 把它带出来就好。

搞定!

5. 搞笑却又应该严肃对待的事情

本来应该结束了,但我猜一定会有人这么干(因为我在其他场景下见过):

注意:下面这个是错误示例!
function wxPromisify(fn) {
    return async function (args) {
        let promise = new Promise();
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        promise = new Promise((resolve, reject) => {
//      ^^^^^^^^^^
            promise.originalResult = fn({ ... });
//          ^^^^^^^^^^^^^^^^^^^^^^
        });

        return promise;
    };
}
로그인 후 복사

这样做不会产生前面提到的 TypeError,但是外面拿到的 Promise 对象却并不携带 originalResult요청 프로세스 중에 요청을 취소해야 하는 경우 wx.request()의 반환 값이 사용됩니다: rrreee

Encapsulated awx.request() wx.request()의 원래 반환 값과 아무 관련이 없는 Promise 객체가 반환됩니다. 요청을 취소하려면 wx.request()의 원래 반환 값을 가져와야 합니다. 🎜rrreee🎜🎜2. 대체 옵션 🎜🎜🎜여기에는 몇 가지 옵션이 있습니다. 🎜
  1. 객체나 배열을 반환하고 구조 분해한 후 사용하세요. 예를 들어 return { promise, originalResult} 또는 [promise, originalResult];
  2. "container" 매개변수를 통해 반환 값을 가져옵니다. awx.request(params, outBox = {})와 같이 처리 중에 outBox에 값을 할당합니다: outBox.originalResult;
  3. JS 이는 동적 유형이며 Promise 객체를 직접 수정하고 속성을 추가할 수 있습니다: promise.originalResult = ....
🎜사용자 관점에서 볼 때 대부분의 경우 원래 반환 값은 필요하지 않습니다. 이때는 반드시 먼저 반환하는 대신 await awx.request()를 사용하는 것이 좋습니다. 구조 분해한 다음 await(또는 then())하므로 첫 번째 방법은 선택 사항이 아닙니다. 🎜🎜두 번째 방법은 원래 반환 값이 필요하지 않은 경우 직접 사용할 수 있습니다. 하지만 원래 반환 값이 필요한 경우에는 먼저 컨테이너 개체를 생성하여 전달해야 합니다. 🎜🎜세 번째 방법은 가장 "편안하게" 사용할 수 있는 방법이어야 합니다. 어떤 경우든 원래 값은 Promise 개체와 함께 표시됩니다. 사용 여부에 관계없이 자유롭게 사용하세요! 🎜🎜이제 세 번째 메서드인 wxPromisify()를 구현해 보겠습니다. 🎜🎜🎜3. 실패한 시도 🎜🎜🎜처음에는 매우 간단하다고 생각했기 때문에 새 Promise( ), 이제 임시 변수를 추가할 수 있습니다: 🎜rrreee🎜 그런 다음 오류가 발생합니다: 🎜rrreee🎜 이 오류는 이해하기 쉽고 수정하기 쉽지만... 실제로 만들기 쉽습니다! 🎜🎜원래는 promise가 지역 변수이고 직접 접근이 가능해서 그 하위 범위에서 사용해도 문제 없을 줄 알았습니다. 그러나 이 하위 범위가 생성자에 있다는 점은 여기서 무시됩니다. 대략적으로 분석해 보겠습니다. 🎜🎜new Promise()에는 매개 변수로 함수(factory라고 가정)가 필요하지만 이 의 실행 타이밍이 공장은 무엇인가요? new Promise()가 Promise 인스턴스를 생성한 후에는 이 인스턴스의 어떤 메서드도 적극적으로 호출하지 않으므로 구성 프로세스 중에 factory가 실행된다는 결론을 내릴 수 있습니다. 즉, 현재로서는 Promise 인스턴스가 아직 생성되지 않았으며, promise정의되지 않음을 참조합니다. 🎜🎜🎜4. 성공적인 시도🎜🎜🎜이제 문제를 알았으니 분석을 계속하겠습니다. 🎜🎜factory는 Promise 인스턴스 생성 중에 호출되며, factory는 함수 본문에서 fn을 직접 실행하며 를 얻을 수 있습니다. > 즉시 code>fn의 반환 값이므로 Promise 인스턴스가 생성된 후 원래 반환 값을 얻을 수 있습니다. 🎜🎜이제 코드를 수정해 보겠습니다. 🎜rrreee🎜 new Promise() 다음에 promise.originalResult에 값을 할당해야 하며 이 "값"은 new Promise() 과정에서 지역 변수 originalResult를 추가하면 됩니다. 🎜🎜완료! 🎜🎜🎜5. 웃기지만 진지하게 받아들여야 할 일 🎜🎜🎜 끝났어야 했는데 누군가 할 것 같아요(다른 상황에서 본 적이 있기 때문입니다): 🎜🎜참고: 다음은 예입니다. 오류! 🎜rrreee🎜이렇게 하면 위에서 언급한 TypeError가 발생하지 않지만 외부에서 얻은 Promise 개체는 originalResult를 전달하지 않습니다. 구체적인 이유는 위의 실패한 시도와 동일하므로 자세히 설명하지 않겠습니다. 🎜두 개의 Promise 개체가 여기에서 생성됩니다🎜. 🎜

6. 다시 얘기해보겠습니다

이번에는 wx.request() 为例,其返回值的主要用途是提供 .abort() 方法用于取消请求。这个应用场景其实和 Axios 处理“取消请求 (Cancellation)”类似,所以不妨参考 Axios 通过 cancelToken 实现的方法。cancelToken를 기반으로 원래 반환 값을 가져옵니다. 핵심은 위에서 언급한 두 번째 방법인 "컨테이너" 객체를 전달하여 필요한 것을 가져오는 것입니다. Promise 객체를 통해 가져오는 것은 본질적으로 특별한 "컨테이너" 객체를 통해 가져오는 것과 동일하므로 더 이상 설명하지 않겠습니다.

추천 튜토리얼: "WeChat 미니 프로그램"

위 내용은 비동기 캡슐화 개선: 반환 값으로 비동기 호출 처리 - Border City Inn의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!