- 回调函数
假定有两个函数f1和f2,后者等待前者的执行结果。
f1(); f2();
如果f1是一个很耗时的任务,可以考虑改写为f1,把f2写为f1的回调函数。
执行的时候就变成了f1(f2); 采用这种方法把同步变成异步,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时都部分推迟执行。function f1(callback){ setTimeout(function(){ //f1代码 callback(); },1000); }
回调函数优点:简单、容易理解和部署;缺点:不利于代码的阅读和维护。
一个同步(阻塞)中使用回调的例子,目的是在func1代码执行完成后执行func2代码。var func1 = function(callback){ // do something (callback && typeof(callback) === "function") && callback(); } func1(func2); var func2 = function(){}
更多回调函数请参考:
- 事件监听
任务的执行不取决于代码的顺序,而取决于某件事件是否发生。
为f1绑定一个事件:f1.on('done', f2);
当f1发生done事件,就执行f2。然后对f1进行改写为
优点:比较容易理解,绑定多个事件,每个事件可以指定多个回调函数,去耦合,实现模块化。function f1(){ setTimeout(function*(){ // f1的任务代码 f1.trigger('done'); },1000); }
- 发布/订阅
例如:jquery中一个插件:Tiny Pub/Sub- f2向信号中心"jquery订阅"done信号: jquery.subscribe("done", f2);
- f1函数如下:
jquey.publish('done');就是f1执行完毕之后,向信号中心发送done信号,从而引发f2的执行。f2执行后,也可以取消订阅function f1(){ setTimeout(function(){ // f1的执行代码 jquey.publish('done'); }, 1000); }
jquery.unsubscribe("done", f2);
- Promises对象:为异步编程提供统一接口。
思想:每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指向回调函数。
Promise的三种状态:- Pending:Promise对象实例创建时候的初始状态。
- Fulfilled:成功的状态
- Rejected:失败的状态
Promise ---> resolved ---> then(回调callback) 或者 ---> rejected ---> catch(回调callback)。Promise一旦从等待状态变成其他状态就永远不能改变状态了。
构造函数内部的代码立刻执行。const instance = new Promise((resolve, reject) => { // 一些异步操作 if(/*异步操作成功*/) { resolve(value); } else { reject(error); } } }) instance.then(value => { // do something... }, error => { // do something... })
then 方法会返回一个新的 Promise 实例,可以分两种情况来看:- 指定返回值是新的 Promise 对象,如return new Promise(...),这种情况没啥好说的,由于返回的是 Promise,后面显然可以继续调用then方法。
- 返回值不是Promise, 如:return 1 这种情况还是会返回一个 Promise,并且这个Promise 立即执行回调 resolve(1)。所以仍然可以链式调用then方法。(注:如果没有指定return语句,相当于返回了undefined)。
- 示例一
function sayHi(name) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(name); }, 2000) }) } sayHi('张三') .then(name => { console.log(`你好, ${name}`); return sayHi('李四'); // 最终 resolved 函数中的参数将作为值传递给下一个then }) // name 是上一个then传递出来的参数 .then(name => { console.log(`你好, ${name}`); return sayHi('王二麻子'); }) .then(name => { console.log(`你好, ${name}`); }) // 你好, 张三 // 你好, 李四 // 你好, 王二麻子
- 示例二
read('./file-01.txt', 'utf8') .then(data => { // 因为 read 方法会返回一个 promise ,返回read执行相当于返回一个 promise // 会将这个 promise 的执行成功的结果传递给下一次 then 的 resolve return read(data, 'utf8'); }, err => { console.log(err); }) .then(data => { // 如果返回一个普通的值 ,会将这个普通值传递倒下一次 then 的成功的参数 return [data]; }, err => { console.log(err); }) .then(data => { // 返回的是一个普通值 console.log(data); // 没有写 return ,相当于返回一个 undefined ,下一个then的成功值则为 undefined }, err => { console.log(err); }) .then(data => { // 上一个 then 没有返回值,默认值为 undefined // undefined 也算成功 console.log(data); // 抛出错误,将传给下一个 then 的 reject throw new Error('xxx'); }, err => { console.log(err); }) .then(null, err => { // 如果上一个 then 抛出错误,最近的 reject 会执行 // reject 执行后默认下一个 then 会接收 undefined console.log(err); }) .then(data => { // 上一个 then 中失败没有返回值,默认为 undefined console.log('resolve'); }, err => { console.log('reject'); });
- 示例三:使用catch代替err
read('./file-01.txt', 'utf8') .then(data => { return read(data, 'utf8'); }) .then(data => { return [data]; }) .then(data => { console.log(data); }) .then(data => { console.log(data); // 抛出错误后,找到最近的接收错误方法 // 如果所有的 then 都没有 reject 方法,则找最后一个 catch throw new Error('xxx'); }) .then(null) .then(data => { console.log('resolve'); }) .catch(err => { console.log(err); });
- async/await
- async/await是基于Promise实现的,不能用于普通的回调函数。
- 和Promise一样,是非阻塞的。
- 使异步代码看起来像是同步代码。
- 一个函数如果加上async,那么该函数就会返回一个Promise,(如果指定的返回值不是Promise对象,也返回一个Promise,只不过立即 resolve ,处理方式同 then 方法,因此 async 函数通过 return 返回的值,会成为 then 方法中回调函数的参数。单独一个 async 函数,其实与Promise执行的功能是一样的。
- await 就是异步等待,它等待的是一个Promise,因此 await 后面应该写一个Promise对象,如果不是Promise对象,那么会被转成一个立即 resolve 的Promise
- 实例一
let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } async function readResult(params) { try { let p1 = await read(params, 'utf8') //await后面跟的是一个Promise实例, //await返回的结果直接return的结果,不需要进行新的then操作。 let p2 = await read(p1, 'utf8') let p3 = await read(p2, 'utf8') console.log('p1', p1) console.log('p2', p2) console.log('p3', p3) return p3 } catch (error) { console.log(error) } } readResult('1.txt').then( // async函数返回的也是个promise data => { console.log(data) }, err => console.log(err) ) // p1 2.txt // p2 3.txt // p3 结束 // 结束
- 示例二
function readAll() { read1() read2()//这个函数同步执行 } async function read1() { let r = await read('1.txt','utf8') console.log(r) } async function read2() { let r = await read('2.txt','utf8') console.log(r) } readAll() // 2.txt 3.txt
- 示例三
async function func() { try { const num1 = await 200; console.log(`num1 is ${num1}`); const num2 = await Promise.reject('num2 is wrong!'); console.log(`num2 is ${num2}`); const num3 = await num2 + 100; console.log(`num3 is ${num3}`); } catch (error) { console.log(error); } } func(); // num1 is 200 // 出错了 // num2 is wrong!
参考:
- Javascript异步编程的4种方法
- 异步方法的发展流程
- JS 异步编程六种方案
网友评论