>  기사  >  위챗 애플릿  >  WeChat 개발에서 async/await 사용

WeChat 개발에서 async/await 사용

hzc
hzc앞으로
2020-06-06 17:36:372648검색

WeChat 미니 프로그램에는 wx.login(), wx.request(), wx.getUserInfo() 등과 같은 비동기 호출인 인터페이스가 많이 있으며, 모두 개체를 매개변수로 사용하고 정의합니다. 다양한 비동기 호출 사례에서 성공(), 실패() 및 완료()를 콜백으로 사용합니다.

그러나 콜백 형식으로 프로그램을 작성하는 것은 정말 귀찮습니다. 이러한 작업을 순차적으로 수행해야 하는 프로세스가 있는 경우:

  • wx.getStorage() 캐시된 데이터 가져오기 및 로그인 상태 확인

  • wx.getSetting () 구성 정보 가져오기,

  • wx.login() 구성 정보를 사용하여 로그인

  • wx.getUserInfo() 로그인 후 사용자 정보 가져오기

  • wx.request() 비즈니스 서버에 데이터 요청

그러면 코드는 아마 이렇게 보일 겁니다

wx.getStorage({
    fail: () => {
        wx.getSetting({
            success: settings => {
                wx.login({
                    success: ({ code }) => {
                        wx.getUesrInfo({
                            code,
                            success: (userInfo) => {
                                wx.request({
                                    success: () => {
                                        // do something
                                    }
                                });
                            }
                        });
                    }
                });
            }
        });
    }
});

분명히 async/await를 사용하면 동일한 로직의 코드를 훨씬 더 편안하게 만들 수 있습니다. 그러나 기본적으로 "WeChat 개발자 도구"는 async/await를 지원하지 않습니다. 활성화하는 방법은 무엇입니까?

1. async/await 사용


관심이 있는 경우 공식 WeChat 애플릿 문서에서 async를 검색하면 "도구 ⇒ 개발 지원 ⇒ 코드 컴파일" 페이지에 언급된 async/await 지원을 찾을 수 있습니다. , 이는 "컴파일 추가" 섹션의 표에서 발췌한 내용입니다.

1.02.1904282 및 이후 버전의 개발 도구에는 ES6를 다음으로 변환하는 기능을 향상시키기 위해 향상된 컴파일 옵션이 추가되었습니다. ES5를 활성화한 후 새로운 컴파일 로직을 제공하고 개발자가 사용할 수 있는 추가 옵션을 제공합니다.

  • async/await 구문을 지원하고, 요청 시 regeneratorRuntime을 주입하며, 디렉터리 위치가 보조 기능과 일치합니다.

간단히 말하면 "WeChat 개발자 도구"가 v1.02.1904282로 업데이트되거나 위에서는 npm install regenerator와 같은 작업을 수행할 필요가 없으며 async/await 기능을 사용하도록 구성 항목을 수정하기만 하면 됩니다. 이 구성은 "도구 모음⇒세부 정보⇒로컬 설정" 페이지에 있습니다.

WeChat 개발에서 async/await 사용

async/await가 사용 가능한지 빠르게 확인하려면 app.js의 onLaunch() 이벤트 함수에 코드 조각을 추가하세요.

(async () => {    const p = await new Promise(resolve => {
        setTimeout(() => resolve("hello async/await"), 1000);
    });    console.log(p);
})();

짧은 자동 컴파일 실행 후 다음에서 확인할 수 있습니다. 디버거 인터페이스의 콘솔 탭 출력:

hello async/await

그렇지 않은 경우 먼저 "WeChat 개발자 도구" 버전을 확인하세요. 적어도 최신 버전을 다운로드하는 데에는 문제가 없습니다.

2. wx.abcd 비동기 메서드 변환


async/await가 지원되지만 wx.abcd()는 Promise 스타일로 캡슐화되어야 합니다.

Node.js는 Node.js 스타일 콜백을 Promise 스타일로 변환하기 위해 util 모듈에서 promisify를 제공하지만 분명히 wx 스타일에서는 작동하지 않습니다. 예를 들어, wx 스타일 비동기 호출은 모두 형식이 일관됩니다. 특징은 다음과 같습니다.

  • 객체를 사용하여 세 가지 주요 콜백을 포함한 모든 매개변수를 전달합니다.

  • success: (res) => 비동기 메서드가 성공할 때의 모든 콜백

  • fail: (err) => 비동기 메서드가 실패할 때의 모든 콜백

  • complete: () => 비동기 메서드가 실패할 경우 완료 시 콜백(성공 또는 실패에 관계 없음)

따라서 wx.abcd()가 Promise 스타일로 변경되고 async/await를 통해 작성되면 아마도 다음과 같을 것입니다

try {
    const res = wx.abcd();
    // do anything in success callback
} catch (err) {
    // do anything in fail callback
} finally {
    // do anything in complete callback
}

Of 물론, catch 및 finally 두 가지가 있습니다. 이 부분은 필요하지 않습니다. 즉, try 문 블록을 사용할 필요가 없습니다. 그러나 catch를 사용하지 않으면 함정이 발생하는데 이에 대해서는 나중에 다루겠습니다. 이제 가장 먼저 해야 할 일은 변화입니다.

2.1. 정의 promisify()

promisify() 就是一个封装函数,传入原来的 wx.abcd 作为参加,返回一个 Promise 风格的新函数。代码和解释如下:

function promisify(fn) {
    // promisify() 返回的是一个函数,
    // 这个函数跟传入的 fn(即 wx.abcd) 签名相同(或兼容)
    return async function(args) {
    //                    ^^^^ 接受一个单一参数对象
        return new Promise((resolve, reject) => {
    //             ^^^^^^^^^^^ 返回一个 Promise 对象
            fn({
    //      ^^ ^ 调用原函数并使用改造过的新的参数对象
                ...(args || {}),
    //          ^^^^^^^^        这个新参数对象得有原本传入的参数,
    //                      ^^  当然得兼容没有传入参数的情况
                success: res => resolve(res),
    //          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  注入 success 回调,resovle 它
                fail: err => reject(err)
    //          ^^^^^^^^^^^^^^^^^^^^^^^^ 注入 fail 回调,reject 它
            });
        });
    };
}

举例使用它:

const asyncLogin = promisify(wx.login);  // 注意别写成 wx.login(),为什么,我不说
try {
    const res = asyncLogin();
    const code = res.code;
    // do something with code
} catch (err) {
    // login error
} finally {
    // promisify 里没有专门注入 complete 回调,
    // 因为 complete 的内容可以写在这里
}

2.2. 定义 wx.async()

不过老实说,把要用的异步方法通过 promisify 一个个处理,写起来还是挺烦的,不如写个工具函数把要用的方法一次性转换出来。不过一查,wx 下定义了不知道多少异步方法,还是退而求其次,用到啥转啥,不过可以批量转,转出来的结果还是封装在一个对象中。整个过程就是迭代处理,最后把每个处理结果聚焦在一起:

function toAsync(names) {    // 这里 names 期望是一个数组
    return (names || [])
        .map(name => (
            {
                name,
                member: wx[name]
            }
        ))
        .filter(t => typeof t.member === "function")
        .reduce((r, t) => {
            r[t.name] = promisify(wx[t.name]);
            return r;
        }, {});
}

这个 toAsync 的用法大致是这样的

const awx = toAsync(["login", "request"]);
await awx.login();
await awx.request({...});

有些人可能更习惯单个参数传入的方式,像这样

const awx = toAsync("login", "request");

那么在 toAsync 的定义中,参数改为 ...names 就好,即

function toAsync(...names) { ... }

还没完,因为我不想在每一个 JS 文件中去 import { toAsync } from ...。所以把它在 App.onLaunch() 中把它注入到 wx 对象中去,就像这样

App({
    onLaunch: function() {
        // ...
        wx.async = toAsync;
        // ...
    }
});

3. await 带来的神坑

工具准备好了,代码也大刀阔斧地进行了改造,看起来舒服多了,一运行却报错!为什么???

先来看一段原来的代码,是这样的

wx.getStorage({
    key: "blabla",
    success: res => {
        // do with res
    }
});

改造之后是这样

const res = await awx.getStorage({ key: "blabla" });  // <== runtime error
// do with res

awx.getStorage 抛了个异常,原因是叫 "blabal" 的这个数据不存在。

为什么原来没有错,现在却报错?

因为原来没有定义 fail 回调,所以错误被忽略了。但是 promisify() 把 fail 回调封装成了 reject(),所以 awx.getStorage() 返回的 Promise 对象上,需要通过 catch() 来处理。我们没有直接使用 Promise 对象,而是用的 await 语法,所以 reject() 会以抛出异常的形式体现出来。

用人话说,代码得这样改:

try {
    const res = await awx.getStorage({ key: "blabla" });  // <== runtime error
    // do with res
} catch (err) {
    // 我知道有错,就是当它不存在!
}

伤心了不是?如果没有伤心,你想想,每一个调用都要用 try ... catch ... 代码块,还能不伤心吗?

3.1. 忽略不需要处理的错误

处理错误真的是个好习惯,但真的不是所有错误情况都需要处理。其实要忽略错误也很简单,直接在每个 Promise 形式的异步调后面加句话就行,比如

const res = await awx
    .getStorage({ key: "blabla" })
    .catch(() => {});
//  ^^^^^^^^^^^^^^^^ 捕捉错误,但什么也不干

稍微解释一下,在这里 awx.getStorage() 返回一个 Promise 对象,对该对象调用 .catch() 会封装 reject 的情况,同时它会返回一个新的 Promise 对象,这个对象才是 await 等待的 Promise。

不过感觉 .catch(() => {}) 写起来怪怪的,那就封装成一个方法吧,这得改 Promise 类的原形

Promise.prototype.ignoreError = function() {
    return this.catch(() => { });
};

这段代码放在定义 toAsync() 之前就好。

用起来也像那么回事

const res = await awx
    .getStorage({ key: "blabla" })
    .ignoreError();

对于单个 await 异步调用,如果不想写 try ... catch ... 块,还可以自己定义一个 ifError(fn) 来处理错误的情况。但是如果需要批量处理错误,还是 try ... catch ... 用起顺手:

4. 回到开始

try {
    const storeValue = await awx.getStorage({});
    const settings = await awx.getSetting();
    const { code } = await awx.login();
    const userInfo = await awx.getUserInfo({ code });
} catch (err) {
    // 处理错误吧
}

看,不需要对每个异步调用定义 fail 回调,一个 try ... catch ... 处理所有可能产生的错误,这可不也是 async/await 的优势!

推荐教程: 《微信小程序

위 내용은 WeChat 개발에서 async/await 사용의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제