1.背景

异步编程对 ArkTS 这门语言来说实在太重要。因为ArkTS是单线程模型【单线程模型指的是,JavaScript只在一个线程上运行。也就是说,JavaScript同时只能执行一个任务,其他任务都必须在后面排队等待。】

如果没有异步编程,很容易就写出回调地狱般的屎山代码。现在在ArkTS中要实现异步并发任务时,最合适的就是使用Promise和async/await【在此之前JS异步的发展历程是callback->promise/generator->async/await】

Promise和async/await提供异步并发能力,是标准的JS异步语法。异步代码会被挂起并在之后继续执行,同一时间只有一段代码执行,适用于单次I/O任务的场景开发,例如一次网络请求【底层是Native 线程】、一次文件读写【底层是Native 线程】等操作。

异步语法是一种编程语言的特性,允许程序在执行某些操作时不必等待其完成,而是可以继续执行其他操作。

2. Promise 是什么

用人话来讲:Promise是用来解决异步回调的一个语法糖【注意:这里的异步和线程是两回事注意:这里的异步和线程是两回事注意:这里的异步和线程是两回事,你在Promise里面写一个耗时操作也还是会阻塞主线程,想要解决只能使用线程,例如taskpool和workerTaskPool和Worker的对比】,这个语法糖还有一个业内通用的规范【Promises/A+规范】,利用这个规范你也可以自己手搓一个 Promise 【参考资料:https://www.ituring.com.cn/article/66566

用专业术语来讲:Promise 是 ECMAScript 2015 (ES6) 中引入的一个新的异步编程特性。【ES 是什么可以点这里查看:https://juejin.cn/post/7338783017123020812
在 ES6 之前,JavaScript 使用回调函数来处理异步操作,这种方式容易导致"回调地狱"的问题。Promise 的引入旨在解决这个问题,提供了一种更优雅和可读性更强的方式来处理异步操作。

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

鸿蒙文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/async-concurrency-overview-V5

2.1 基本语法

2.1.1 创建Promise对象

const promise: Promise<void> = new Promise((resolve: Function, reject: Function) => {
    // 异步处理
    // 处理结束后、调用resolve(成功) 或 reject(失败)
})

2.1.2 使用Promise对象

2.1.2.1 promise.then

对通过new生成的promise对象为了设置其值在 resolve(成功) / reject(失败)时调用的回调函数 可以使用promise.then(onFulfilled, onRejected) 实例方法。

const promise: Promise<void> = new Promise((resolve: Function, reject: Function) => {
    // 异步处理
    // 处理结束后、调用resolve(成功) 或 reject(失败)
    setTimeout(() => {
      const randomNumber: number = Math.random();
      if (randomNumber > 0.5) {
        resolve(`${randomNumber}`);
      } else {
        reject(new Error('随机数太小'));
      }
    }, 1000);
})

promise.then((onFulfilled: string) => {
  console.info(`resolveResult ${resolveResult}`);//随机输出随机数
}, (onRejected: string) => {
  console.info(`rejectResult ${rejectResult}`);//随机抛出 随机数太小
})

resolve(成功)时
onFulfilled 会被调用
reject(失败)时
onRejected 会被调用
onFulfilled、onRejected 两个都为可选参数。
比如可以只实现onFulfilled

const promise: Promise<void> = new Promise((resolve: Function, reject: Function) => {
    // 异步处理
    // 处理结束后、调用resolve(成功) 
    setTimeout(() => {
      const randomNumber: number = Math.random();
      resolve(`${randomNumber}`);
    }, 1000);
})

promise.then((resolveResult: string) => {
  console.info(`resolveResult ${resolveResult}`);
})

2.1.2.2

另外一种语法是可使用promise.catch 拿到 onRejected 的结果,例如

const promise: Promise<string> = new Promise((resolve: Function, reject: Function) => {
      // 异步处理
    // 处理结束后、调用resolve(成功) 或 reject(失败)
    setTimeout(() => {
      const randomNumber: number = Math.random();
      if (randomNumber > 0.5) {
        resolve(`${randomNumber}`);
      } else {
        reject(new Error('随机数太小'));
      }
    }, 1000);
})
promise.then((resolveResult: string) => {
  console.info(`resolveResult ${resolveResult}`);
}).catch((catchRejectResult: string) => {
  console.error(`rejectResult ${catchRejectResult}`);
});
//会输出
//rejectResult >0.5 的数值
//或者输出
//rejectResult 随机数太小

如果同时实现了then.promise onRejected 和promise.catch。则promise.catch会不生效,例如:

const promise: Promise<string> = new Promise((resolve: Function, reject: Function) => {
  setTimeout(() => {
    try {
      throw new Error('随机数太小')
      resolve("成功了")
    } catch (e) {
      reject(e)
    }
  }, 1000);
})
promise.then((resolveResult: string) => {
  console.info(`resolveResult ${resolveResult}`);
},(catchRejectResult: Error)=>{
  console.error(`rejectResult ${catchRejectResult.message}`);
}).catch((catchRejectResult: Error) => {
  console.error(`catch rejectResult ${catchRejectResult.message}`);
})
//只会输出onRejected 中的回调,不会输出 catch中的
rejectResult 随机数太小

2.1.2.3 promise.finally

Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现【reject】或拒绝【resolve】)时调用的函数。
例如我们可以在网络请求成功或者失败后关闭loading时使用这个方法

const promise: Promise<string> = new Promise((resolve: Function, reject: Function) => {
  //发起网络请求
})
promise.then((netResult: string) => {
  //网络请求成功
}).catch((netErrorResult: Error) => {
  //网络请求失败
}).finally(()=>{
  //网络请求结束
});

2.1.3 Promise的状态

用new Promise 实例化的promise对象有以下三个状态。

  • "has-resolution" - Fulfilled【已兑现状态】

resolve(成功)时。此时会调用 onFulfilled

  • "has-rejection" - Rejected【已拒绝】

reject(失败)时。此时会调用 onRejected

  • "unresolved" - Pending【待定】

既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等

为了保证程序的健壮性,Promise 里面的实现最好使用try catch 保证安全,因为在Arkts中,如果出现异常,则会导致App闪退【在Web端中不会】

2.1.3.1 错误的写法【模拟】

//这个写法会导致APP闪退
const promise: Promise<string> = new Promise((resolve: Function, reject: Function) => {
  setTimeout(() => {
    throw new Error('随机数太小')
  }, 1000);
})

img

2.1.3.2 正确的写法【模拟】

const promise: Promise<string> = new Promise((resolve: Function, reject: Function) => {
  setTimeout(() => {
    try {
      throw new Error('随机数太小')
      resolve("成功了")
    } catch (e) {
      reject(e)
    }
  }, 1000);
})
promise.then((resolveResult: string) => {
  console.info(`resolveResult ${resolveResult}`);
}).catch((catchRejectResult: string) => {
  console.error(`rejectResult ${catchRejectResult}`);
});

//会输出
rejectResult Error: 随机数太小

2.1.4 链式调用

promise.then 之后会返回一个 Promise 对象,可以进行下一个promise任务。在一些复杂业务上我们可以拆分多个子任务,依次向下执行

链式调用的几种写法:

2.1.4.1 Promise原始链式调用-写法1

new Promise<number>((resolve, reject) => {
  resolve(1)
}).then(data => {
  console.log("第一次对结果处理");
  return new Promise<number>((resolve, reject) => {
    resolve(data * 2);
  });
}).then(data => {
  console.log("第二次对结果处理");
  return new Promise<number>((resolve, reject) => {
    resolve(data * 3);
  });
}).then(data => {
  console.log("第三次对结果处理");
  data *= 4;
  console.log("Promise的链式调用结束,最终结果:", data);
}).catch((err: Error) => {
  console.log("链式调用中某个环节出现错误!!", err);
}).finally(()=>{
  console.log("链式调用结束");
})

第一次对结果处理
第二次对结果处理
第三次对结果处理
Promise的链式调用结束,最终结果: 24
链式调用结束

2.1.4.2 Promise原始链式调用-写法2

new Promise<number>((resolve, reject) => {
  setTimeout(() => {
    console.log("请求结果:", 1);
    resolve(1);
  }, 1000)
}).then(data => {
  console.log("第一次对结果处理");
  return Promise.resolve(data * 2);
}).then(data => {
  console.log("第二次对结果处理");
  return Promise.resolve(data * 3);
}).then(data => {
  console.log("第三次对结果处理");
  data *= 4;
  console.log("Promise的链式调用结束,最终结果:", data);
}).catch((err: Error) => {
  console.log("链式调用中某个环节出现错误!!", err);
}).finally(()=>{
  console.log("链式调用结束");
})

第一次对结果处理
第二次对结果处理
第三次对结果处理
Promise的链式调用结束,最终结果: 24
链式调用结束

2.1.4.3 Promise原始链式调用-写法3

new Promise<number>((resolve, reject) => {
  setTimeout(() => {
    console.log("请求结果:", 1);
    resolve(1);
  }, 1000)
}).then(data => {
  console.log("第一次对结果处理");
  return data * 2
}).then(data => {
  console.log("第二次对结果处理");
  return data * 3
}).then(data => {
  console.log("第三次对结果处理");
  data *= 4;
  console.log("Promise的链式调用结束,最终结果:", data);
}).catch((err: Error) => {
  console.log("链式调用中某个环节出现错误!!", err);
}).finally(()=>{
  console.log("链式调用结束");
})

第一次对结果处理
第二次对结果处理
第三次对结果处理
Promise的链式调用结束,最终结果: 24
链式调用结束

2.1.4.4 Promise原始链式调用-写法4

Promise.all([
  new Promise<number>((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    },2000)
  }), new Promise<number>((resolve, reject) => {
  setTimeout(() => {
    resolve(2)
  },100)
})
]).then(results => {
  console.log("与式调用结果数组results:", results);
  console.log("对与式调用结果一起处理操作...");
}).catch((err: Error) => {
  console.log("Promise.all与式调用错误!!", err);
}).finally(()=>{
  console.log("链式调用结束");
})

//与式调用结果数组results: 1,2
//对与式调用结果一起处理操作...
//链式调用结束

2.2 Promise 并发

2.2.1 Promise.all()

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。

  • 当所有输入的 Promise 都被兑现时【resolve】,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。
  • 如果输入的任何 Promise 被拒绝或者异常【reject 或者代码异常】,整体都将将被拒绝,并带有第一个被拒绝的原因。

一句话总结:所有的Promise任务都成功执行,或者只要有一个失败,则才会回调到Promise.all.then

const promise1 = new Promise<number>(resolve => {
  resolve(1)
})
const promise2 = new Promise<Boolean>(resolve => {
  setTimeout(resolve, 500, true);
});
const promise3 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 100, 'RANRAN');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(JSON.stringify(values));
});



//输出 [1,true,"RANRAN"]
const promise1 = new Promise<number>(resolve => {
  resolve(1)
})
const promise2 = new Promise<Boolean>(resolve => {
  setTimeout(resolve, 500, true);
});
const promise3 = new Promise<string>((resolve, reject) => {
  reject("我是一个异常")
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(JSON.stringify(values));
}).catch((err: Object)=>{
  console.error(`${err}`)
});

// 输出 我是一个异常

2.2.2 Promise.allSettled()

Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise
当所有输入的 Promise 都已被兑现或者被拒绝或者异常,返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象[PromiseSettledResult]数组。

const promise1 = new Promise<number>(resolve => {
  resolve(1)
})
const promise2 = new Promise<Boolean>(resolve => {
  setTimeout(resolve, 500, true);
});
const promise3 = new Promise<string>((resolve, reject) => {
  reject("我是一个异常")
});

Promise.allSettled([promise1, promise2, promise3]).then((values) => {
  console.log(JSON.stringify(values));
}).catch((err: Object)=>{
  console.error(`${err}`)
});

//输出
[{"status":"fulfilled","value":1},{"status":"fulfilled","value":true},{"status":"rejected","reason":"我是一个异常"}]

img

img

2.2.3 Promise.any()

Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。

一句话总结:只要有一个Promise任务执行完毕或者所有的Promise任务都执行失败,则才会回调到Promise.any.then

//有2个任务成功,一个任务失败
const promise1 = new Promise<string>((resolve, reject) => setTimeout(reject, 100, 'fail'));
const promise2 = new Promise<string>((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise<string>((resolve) => setTimeout(resolve, 500, 'slow'));

Promise.any([promise1, promise2, promise3]).then((value) =>
  console.log(value)
).catch((error:Error)=>{
  console.error('异常:'+error)
});

//输出 "quick"

//所有任务都失败
const promise1 = new Promise<string>((resolve, reject) => setTimeout(reject, 100, 'fail'));
const promise2 = new Promise<string>((resolve,reject) => setTimeout(reject, 100, 'quick'));
const promise3 = new Promise<string>((resolve,reject) => setTimeout(reject, 500, 'slow'));

Promise.any<string>([promise1, promise2, promise3]).then((value:string) =>
  console.log(value)
).catch((error:Error)=>{
  console.error('异常:'+error)
});

//输出 "异常:AggregateError: All promises were rejected" 

2.2.4 Promise.race()

Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。

一句话总结:只要有任意一个Promise任务执行完毕或者任意一个的Promise任务执行失败,则才会回调到Promise.race.then

//两个promise 成功任务,谁快,谁返回
const promise1 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 100, "two");
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
});

//输出"two"

//一个promise失败任务,一个promise 成功任务,还是谁快,谁返回
const promise1 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise<string>((resolve, reject) => {
  setTimeout(reject, 100, "two");
});

Promise.race([promise1, promise2])
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.error(error);
  });

//输出"异常:two" 

3. async&await

async/await 是一种用于处理异步操作的语法糖,在 ES2017 被引入,用于更简洁地编写基于 Promise 的链式回调代码

使用 async/await 主要是避免了传统的回调函数或 Promise 链的嵌套,可以将Promise的链式调用变为类似同步代码的形式。

以前的异步处理

  1. 现在有一个 requestApi 函数,用于模拟网络请求,传入什么则返回什么

    function requestApi(params) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(params);
      }, 1000);
    });
    }
    
  2. 首先提一个需求,当第一次传入 timi ,拿到请求返回的结果为 timi;第二次需要利用第一次获得的结果去发送网络请求,拿到timiaaa;而第三次再利用第二次的结果发送网络请求,拿到 timiaaabbb

 params: 'timi' ->> res1: 'timitimi'
 params: res1 + 'aaa' ->> res2: 'timiaaa'
 params: res2 + 'bbb' ->> res3: 'timiaaabbb'
  1. 假如没有 async/await
 function requestApi(params) {
   return new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve(params);
     }, 1000);
   });
 }
 
 requestApi('timi').then((res) => {
   requestApi(res + 'aaa').then((res) => {
     requestApi(res + 'bbb').then((res) => {
       console.log(res); // timiaaabbb
     });
   });
 });
  1. 使用 async/await,我们会发现异步的任务变的很清晰,更利于业务代码的编写
async function fetchData() {
   const res1 = await requestApi('timi');
   const res2 = await requestApi(res1 + 'aaa');
   const res3 = await requestApi(res2 + 'bbb');
   console.log(res3); // timiaaabbb
 }
 
 fetchData();

3.1 async

async声明function是一个异步函数【AsyncFunction】,返回一个promise对象,可以使用 then 方法添加回调函数。async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function test() {
  return 'RANRAN';
}
console.log(test.constructor.name); // AsyncFunction
console.log(test()); // Promise { 'RANRAN' }

// async返回的是一个promise对象
test().then(res=>{
  console.log(res); // RANRAN
})

// 如果async函数没有返回值 async函数返回一个undefined的promise对象
async function fn() {
  console.log('没有返回');
}

console.log(fn()); // Promise { undefined }


// 可以看到async函数返回值和Promise.resolve()一样,
// 将返回值包装成promise对象,如果没有返回值就返回undefined的promise对象

3.2 await

await 操作符只能在异步函数 async function 内部使用。如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果,也就是说它会阻塞后面的代码,等待 Promise 对象结果。如果等待的不是 Promise 对象,则返回该值本身。

function test() {
  return new Promise((resolve)=>{
    setTimeout(() => {
        resolve('RANRAN');
    }, 1000);
  })
}
function fn() {
  return 'fn';
}

async function next() {
    let res0 = fn()
    let res1 = await test()
    let res2 = fn()
    console.log(res0);
    console.log(res1);
    console.log(res2);
}
next();

//先输出fn然后等待1s后输出,然后在输出fn
[LOG]: "fn" 
[LOG]: "RANRAN" 
[LOG]: "fn" 

3.3 async&await 常见使用例子

3.3.1 同步依次异步任务

总任务花费时长:3000毫秒

function resolveAfter2Seconds() {
  console.log("开始较慢兑现的 promise");
  console.log("== sequentialStart 开始 ==");

  // 1. 启动一个计时器,并在计时器完成后打印结果
  const slow = resolveAfter2Seconds();
  console.log("任务1结果:"+await slow);

  // 2. 等待前一个计时器完成后,启动下一个计时器
  const fast = resolveAfter1Second();
  console.log("任务2结果:"+await fast);

  console.log("== sequentialStart 结束 ==");
}

sequentialStart()


输出
[LOG]: "== sequentialStart 开始 ==" 
[LOG]: "开始较慢兑现的 promise" 
[LOG]: "较慢兑现的 promise 完成了" 
[LOG]: "任务1结果:slow" 
[LOG]: "开始较快兑现的 promise" 
[LOG]: "较快兑现的 promise 完成了" 
[LOG]: "任务2结果:fast" 
[LOG]: "== sequentialStart 结束 ==" 

3.3.1.1 同步请求网络任务

//这里的XXHttp 是封装的一个网络框架返回的是Promise

let res1 = await XXHttp.POST<Object>(Object, "xxxx", new Map([
  ['name', '张三']
]))

let res2 = await XXHttp.GET<Object>(Object, "xxxx", new Map([
  ['name', '李四']
]))
if (res1.isOK() && res2.isOK()) {
  //两个接口都请求成功要做的事情
} else {
  //任意接口请求失败或者都失败要做的事情
}

3.3.2 同步并行异步任务-写法一

总任务花费时长:2000毫秒

function resolveAfter2Seconds() {
  console.log("开始较慢兑现的 promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("较慢兑现的 promise 完成了");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("开始较快兑现的 promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("较快兑现的 promise 完成了");
    }, 1000);
  });
}

async function sequentialWait() {
  console.log("== sequentialWait 开始 ==");

  // 1. 启动两个计时器,互不等待
  const slow = resolveAfter2Seconds();
  const fast = resolveAfter1Second();

  // 2. 等待较慢的计时器完成后,打印结果
  console.log("任务1结果:"+await slow);
  // 3. 等待较快的计时器完成后,打印结果
  console.log("任务2结果:"+await fast);

  console.log("== sequentialWait 结束 ==");
}

sequentialWait()

//输出
[LOG]: "== sequentialWait 开始 ==" 
[LOG]: "开始较慢兑现的 promise" 
[LOG]: "开始较快兑现的 promise" 
[LOG]: "较快兑现的 promise 完成了" 
[LOG]: "较慢兑现的 promise 完成了" 
[LOG]: "任务1结果:slow" 
[LOG]: "任务2结果:fast" 
[LOG]: "== sequentialWait 结束 ==" 

3.3.3 同步并行异步任务-写法二【推荐用法】

总任务花费时长:2000毫秒

function resolveAfter2Seconds() {
  console.log("开始较慢兑现的 promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("较慢兑现的 promise 完成了");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("开始较快兑现的 promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("较快兑现的 promise 完成了");
    }, 1000);
  });
}

async function concurrent1() {
  console.log("== concurrent1 开始 ==");

  // 1. 并发启动两个计时器,并等待它们完成
  const results = await Promise.all([
    resolveAfter2Seconds(),
    resolveAfter1Second(),
  ]);
  // 2. 同时打印两个计时器的结果
  console.log("任务1结果:"+results[0]);
  console.log("任务2结果:"+results[1]);

  console.log("== concurrent1 完成 ==");
}

concurrent1()

//输出
[LOG]: "== concurrent1 开始 ==" 
[LOG]: "开始较慢兑现的 promise" 
[LOG]: "开始较快兑现的 promise" 
[LOG]: "较快兑现的 promise 完成了" 
[LOG]: "较慢兑现的 promise 完成了" 
[LOG]: "任务1结果:slow" 
[LOG]: "任务2结果:fast" 
[LOG]: "== concurrent1 完成 ==" 

3.4 异常处理

如果await后面的异步操作出错,则会抛出异常,不继续执行后面的逻辑
例如:

async function test() {
  console.log("test 开始")
  let result=await new Promise<string>((resolve, reject) => {
    setTimeout(() => {
      reject("RANRAN");
    }, 1000);
  });
  console.log("test 结束")
}
test();
//只会输出 test 开始,不会输出 test 结束

防止出错的方法,也是将其放在try...catch代码块之中。

async function test() {
    console.log("test 开始")
    try {
      let result=await new Promise<string>((resolve, reject) => {
        setTimeout(() => {
          reject("RANRAN");
        }, 1000);
      });
      console.log(result)
    } catch (e) {
      console.error("异常:"+e)
    }
    console.log("test 结束")
}
//输出
test 开始
异常:RANRAN
test 结束

3.5 优雅的异常处理

从上面的例子我们发现,如果每个请求的地方都添加 try-catch,就会显得代码很臃肿,解决这样的臃肿代码,我们可以用await-to-js函数。
await-to-js 是前端中的一个三方库,它的源码很简单,就 23 行代码,我们一起来看看。

/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function to<T, U = Error>(
  promise: Promise<T>,
  errorExt?: object
): Promise<[U, undefined] | [null, T]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, undefined]>((err: U) => {
      if (errorExt) {
        const parsedError = Object.assign({}, err, errorExt)
        return [parsedError, undefined]
      }

      return [err, undefined]
    })
}

export default to

大致流程如下: 函数to接受参数Promise和errorExt,如果这个 Promise 成功时返回[null, data],如果异常时会判断是否带有errorExt参数(代表传递给 err 对象的附加信息),如果有时会与 catch 捕获的 err 合并返回,如果没有时返回[err, undefined]。

3.5.1 在非ArkTS中的用法

  • 通过to函数改造后,如果返回第一个参数不为空时,说明该请求报错,就可以提前 return 出去,如果不存在第一个参数时,则异步请求成功。
    ``` typescript
    import to from 'await-to-js'

function getList(){
return new Promise((resolve, reject) => {
//我是一个网络请求,可能成功,可能失败
})
}

// 获取列表list 如果err有值,则是异常,否则是成功, 可以继续下方的逻辑
const [err, data] = await to(getList(params))
if (err) return


### 3.5.2 在ArkTS中的用法

上述代码,如果用在ArkTS中,我们会发现无法使用,const [err, data]  这种写法目前在**ArkTS不支持解构变量声明**,为了解决,我们可以使用一个包装类 PromiseResult 来实现

```typescript
/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function safePromise<T, U = Error>(
  promise: Promise<T>,
  errorExt?: object
): Promise<PromiseResult<T, U>> {
  return promise
    .then<PromiseResult<T, U>>((data: T) => new PromiseResult(data, null))
    .catch<PromiseResult<T, U>>((err: U) => {
      if (errorExt) {
        const parsedError = Object.assign({}, err, errorExt);
        return new PromiseResult(undefined, parsedError)
      }
      return new PromiseResult(undefined, err)
    });
}

class PromiseResult<T, U = Error> {
  result: T
  error: U

  constructor(result: T, error: U) {
    this.result = result
    this.error = error
  }
}

export default safePromise;

使用例子

async function test() {
  console.log("test 开始")
  const data = await safePromise(new Promise<string>((resolve, reject) => {
    setTimeout(() => {
      reject("RANRAN");
    }, 1000);
  }));
  if (data.error) {
    console.error("异常了")
  }else{
    console.log("没有异常")
  }
  console.log("test 结束")
}
test();
//输出
test 开始
异常了:RANRAN
test 结束

4. 进阶

如果上述promise不满足复杂场景,那么ReactiveX流式编程99.99%能满足你的需求,在Android中,有RxJava框架,在JS中也有RxJS。有人也把RxJS移植到了ArkTS中
[鸿蒙版rxjs](https://ohpm.openharmony.cn/#/cn/detail/rxjs

关于RxJS的用法可以看这个文档:https://cn.rx.js.org/manual/tutorial.html

5. 其它学习资料

作者:冉冉同学

链接:https://www.nutpi.net/thread?topicId=176

來源:坚果派

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐