异步代码

Posted daheiylx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步代码相关的知识,希望对你有一定的参考价值。

一、Promise

  promise表示一个异步操作的最终结果,可以理解为状态机,它存在三种不同的状态:

(1)Pending:表示还在执行。

(2)Fulfilled(或者resolved):执行成功。

(3)Rejected:执行失败。

1、将异步方法封装成Promise

 1 //Promise的构造函数
 2 var promise = new Promise(function (resolve,reject) {
 3     if(/*异步操作成功*/){
 4         resolve(value);
 5     }else{
 6         reject(value);
 7     }
 8 })
 9 //使用Promise封装的readFile
10 var fs = require("fs");
11 function readFile_promise(path) {
12     return new Promise(function(resolve, reject) {
13         fs.readFile(path, "utf-8", function(err, data) {
14             if (data) {
15                 resolve(data);
16             } else {
17                 reject(err);
18             }
19         });
20     });
21 }

其中,resolve和reject是两个函数,resolve函数会在异步操作成功完成时被调用,并将异步操作的返回值作为参数传递到外部;reject则在异步操作出现异常时被调用,会将错误信息作为参数传递出去。即resolve和reject方法没有做多余的操作,仅仅把异步的结果传递出去而已,对于异步结果,是交给then方法来完成的。

2、使用then方法获取结果

 1 //处理结构
 2 promise.then(function (data) {
 3     //success
 4 },function (error) {
 5     //failure
 6 })
 7 var pro = readFile_promise(‘foo.txt‘);
 8 pro.then(function(value) {
 9     console.log(value);
10     return readFile_promise(‘bar.txt‘);
11 }).then(function(value) {
12     console.log(value);
13 }).catch(function(err) {
14     console.log(err);
15 });//捕获异常

3、Promise的常用API

(1)Promise.resolve:将一个给Promise对象转化为Promise对象。但resolve不能转换一个异步方法,例如readFile方法。

 1 var obj = {
 2     then: function() {
 3         console.log("I am a then method");
 4     }
 5 }
 6 Promise.resolve(obj); //转化后的Promise会自动执行其的then方法
 7 //I am a then method
 8 var p = Promise.resolve("Hello World");
 9 p.then(function (result) {
10     console.log(result);
11 }); //Hello World

(2)Promise.reject:也是返回一个Promise对象,不同之处在于这个Promise的状态为reject,reject方法的参数会作为错误信息传递给then方法。

(3)Promise.all:将多个Promise对象包装成一个Promise。当调用Promise.all时,所有的Promise都已经开始执行了,all方法只是等到全部的Promise完成后,对所有的执行结果做一下包装再返回。

1 var promises = ["foo.txt", "bar.txt", "baz.txt"].map(function(path) {
2     return readFile_promise(path);
3 });
4 Promise.all(promises).then(function(results) {
5     console.log(results); //results的内容是文本文件内容的顺序排列
6 }).catch(function(err) {
7 
8 });

 (4)Promise.race:接收一个Promise数组作为参数并返回一个新的Promise,数组中的Promise会同时开始执行,race返回的Promise的状态由数组中率先执行完毕的Promise的状态决定。

(5)Promise.catch:Promise在执行中如果出了错误,可以使用throw关键字抛出错误,并且可以使用catch方法进行去捕获;如果不设置任何回调函数捕获错误,Promise内部抛出的错误就无法传递到外部。

1 var promise = new Promise(function(resolve, reject) {
2     throw new Error("get error");
3 });
4 //如果不设置catch函数,上面即使抛出error也不会使进程退出。
5 promise.catch(function(error) {
6     console.log(error);
7 });

4、Promise的不足:Promise的一堆链式调用会让人看起来很麻烦。可以考虑将then方法的回调函数抽取出来。

二、Generator

  我们无法控制Promise的执行,新建一个Promise后,其状态自动转化为pending,同时开始执行,直到状态改变后我们才能进行下一步操作。而Generator函数不同,Generator函数可以由用户执行中断或者恢复执行的操作,Generator中断后可以转去执行别的操作,然后再回头从中断的地方恢复执行。

  Generator函数和普通函数在外表上最大的区别有两个:

(1)在function关键字和方法名中间有个星号(*)。

(2)方法体中使用“yield”关键字。

 

 1 var fs = require("fs");
 2 
 3 function readFile_promise(path) {
 4     return new Promise(function(resolve, reject) {
 5         fs.readFile(path, "utf-8", function(err, data) {
 6             if (data) {
 7                 resolve(data);
 8             } else {
 9                 reject(err);
10             }
11         });
12     });
13 }
14 
15 function* gen() {
16     var result = yield readFile_promise(‘../baz.txt‘);
17     console.log(result);
18 }
19 var g = gen();
20 var result = g.next();
21 //console.log(result);
22 result.value.then(function(data) {
23     g.next(data);
24 });

 

之所以可以使用Generator函数来处理异步任务,原因有二:

(1)Generator函数可以中断和恢复执行,这个特性有yield关键字来实现。

(2)Generator函数内外可以交换数据,这个特性由next函数来实现。

即Generator函数处理异步任务的核心思想:先将函数暂停在某处,然后拿到异步操作的结果,然后再把这个结果传到方法体内。

三、async

  async函数可以看作是自带执行器的Generator函数。await关键字后面往往是一个Promise,如果不是就隐式调用Promise.resolve来转换成一个Promise。

 1 var fs = require("fs");
 2 async function asyncFunc() {
 3 
 4     return "hello node";
 5 }
 6 asyncFunc().then(function(data) {
 7     console.log(data);
 8 });
 9 
10 function readFile_promise(path) {
11     return new Promise(function(resolve, reject) {
12         fs.readFile(path, "utf-8", function(err, data) {
13             if (data) {
14                 resolve(data);
15             } else {
16                 reject(err);
17             }
18         });
19     });
20 }
21 
22 aysnc
23 
24 function readFile() {
25     var result = await readFile_promise("../foo.txt");
26     console.log(‘test‘);
27 }
28 readFile();

  async函数的优点:可以实现自动执行,无须借助第三方模块等,也免去了Generator函数中一些复杂的概念,async函数的声明和执行与普通同步函数几乎一模一样(除了async和await关键字外)。

  async函数也有一些不足的地方,如果我们有很多层的方法调用,最底层的异步操作被封装成了async方法,那么该函数的所有上层方法可能都要变成async方法。

 

 

 

  


以上是关于异步代码的主要内容,如果未能解决你的问题,请参考以下文章

单元测试不了解 XCTest 期望的异步 UI 代码?

我应该在 Fragment 中的啥生命周期状态下执行异步任务?

NodeJs异步的执行过程

使用带有 viewpager 的异步任务时的竞争条件

在哪里以及如何使用片段填充我的标签

Android - 为列表视图填充适配器的异步任务