Promise 和 Async / Await 的介绍

1. Promise 是为了解决什么问题?

解决了回调地狱 Callback Hell 的问题。

回调地狱的问题并不只是在于缩进太多(如下图),至少在阅读如下代码的时候不会有什么障碍。

Promise 和 Async / Await 的介绍

1. 难于理解

真正的问题在于逻辑难于理解,如下图代码中,我们假定形如 doSomething 的调用又涉及到一步调用,那么阅读的人可能会需要把调用顺序记在脑袋里才行。

listen( "click", function handler(evt){ 

doSomething1();

doSomething2();

doSomething3();

doSomething4();

setTimeout( function request(){

doSomething8();

doSomething9();

doSomething10();

ajax( "http:// some. url. 1", function response( text){

if (text == "hello") {

handler();

} else if (text == "world") {

request();

}

});

doSomething11();

doSomething12();

doSomething13();

}, 500);

doSomething5();

doSomething6();

doSomething7();

});

相比较起来,如下的一个使用 Promise 包装的例子就很简洁,其关键点在于 Promise 的构造函数中,只负责成功/失败的通知,而后续的操作放在了 then 中

const getJSON = function(url) {

const promise = new Promise(function(resolve, reject){

const handler = function() {

if (this.readyState !== 4) {

return;

}

if (this.status === 200) {

resolve(this.response);

} else {

reject(new Error(this.statusText));

}

};

const client = new XMLHttpRequest();

client.open("GET", url);

client.onreadystatechange = handler;

client.responseType = "json";

client.setRequestHeader("Accept", "application/json");

client.send();

});

return promise;

};

getJSON("/posts.json").then(function(json) {

console.log('Contents: ' + json);

}, function(error) {

console.error('出错了', error);

});

2. 信任问题

经过 Promise 流程的调用,将同步调用放在 then 中就不会出现同步调用意外的早于异步调用的情况;而且 Promise 的结果不能被篡改,多次调用的回调结果都能保持一致。

2. 如何模拟实现 Promise

let status = 'pending';

class Promise {

constructor(func){

func(this._resolve.bind(this), this._reject.bind(this))

}

_resolve() {

status = 'fullfilled';

}

_reject() {

status = 'rejected';

}

then(succCallback, failCallback) {

if(status == 'pending') {

this.succCbList.push(succCallback);

this.failCbList.push(failCallback);

} else if(status== 'fullfilled'){

succCbList.forEach(f => {f();})

} else {

failCallback.forEach(f => {f();})

}

}

}

3. 实际的应用场景

service.interceptors.response.use(

response => {

const res = response.data;

if (res.status == 10101 || res.status == 401) {

G.U.clearCookie()

G.U.removeCookie('token', document.domain)

G.U.removeCookie('token', G.U.getTopDomain())

Message.error({

message: res.message || '未授权用户,即将跳转到登录界面',

duration: 2 * 1000

})

setTimeout(() => {

window.location.href = '/login.html'

}, 2000)

} else if (res.status == 404) {

Message.error({

message: res.message || '请求找不到',

duration: 5 * 1000

})

} else if (res.status == 500 || res.status == 503) {

Message.error({

message: res.message || '服务器错误',

duration: 5 * 1000

})

} else if (G.U.isBlob(res)) {

// do nothing

} else if (res.status != 0) {

Message.error({

message: res.message || res.errorMessage || '未知异常',

duration: 5 * 1000

})

}

return response.data

},

error => {

Message.error({

message: error.message,

duration: 5 * 1000

})

}

)

service.interceptors.response.use(

response => {

const res = response.data;

let message;

return new Promise((resolve, reject) => {

if (res.status == 10101 || res.status == 401) {

G.U.clearCookie()

G.U.removeCookie('token', document.domain)

G.U.removeCookie('token', G.U.getTopDomain())

setTimeout(() => { window.location.href = '/login.html' }, 2000)

message = res.message || '未授权用户,即将跳转到登录界面';

} else if (res.status == 404) {

message = res.message || '请求找不到';

} else if (res.status == 500 || res.status == 503) {

message = res.message || '服务器错误';

} else if (G.U.isBlob(res)) {

// do nothing

} else if (res.status != 0) {

message = res.message || res.errorMessage || '未知异常'

}

if(message != undefined) {

reject(message);

Message.error({ message, duration: 3 * 1000 })

} else {

resolve(response.data);

}

});

},

error => {

Message.error({ message: error.message, duration: 5 * 1000 })

return Promise.reject(error.message)

}

)

返回的 Promise 对象,可以让调用的位置,按照 Promise 的缘分书写 then/catch。下面是来自 components/activity/view.vue 使用 Promise 改写后的代码,后者看看起来更简洁:

methods: {

queryAuditStatus(id){

G.R.Common.getAuditProgress('ACTIVITY_AUDIT', id).then(resp => {

if(resp.status == 0) {

this.processData = resp.data;

}

})

}

},

mounted() {

G.R.Product.getActivityDetail(params.id).then(resp => {

if(resp.status == 0 && resp.data) {

this.product = Activity.parse(resp.data);

this.queryAuditStatus(params.id);

} else{

this.$message({

type:'info',

message:resp.message

})

}

this.loading = false;

})

}

let id = G.U.getParam('id', this);

this.loading = true;

G.R.Product.getActivityDetail(id).then(resp => {

this.product = Activity.parse(resp.data || {});

return G.R.Common.getAuditProgress('ACTIVITY_AUDIT', id);

}).then(resp => {

this.processData = resp.data;

}).finally(() => {

this.loading = false;

});

4.Async/Await 的含义和基本用法

1. Generator

可以把 Generator 函数看成是一个状态机,封装了多个内部状态,执行 Generator 函数会返回一个遍历器对象,代表 Generator 函数的内部指针。虽然 Generator 函数是一个普通函数,但是有两个特征:

  • function 关键字与函数名之间有一个星号;
  • 函数体内部使用 yield 表达式,定义不同的内部状态。

function* helloWorldGenerator() {

yield 'hello';

yield 'world';

return 'ending';

}

var hw = helloWorldGenerator();

hw.next()

// { value: 'hello', done: false }

hw.next()

// { value: 'world', done: false }

hw.next()

// { value: 'ending', done: true }

hw.next()

// { value: undefined, done: true }

Async/Await

async 就是 Generator 函数的语法糖。如下是使用 Generator 函数依次读取两个文件的代码:

const fs = require('fs');

const readFile = function (fileName) {

return new Promise(function (resolve, reject) {

fs.readFile(fileName, function(error, data) {

if (error) return reject(error);

resolve(data);

});

});

};

const gen = function* () {

const f1 = yield readFile('/etc/fstab');

const f2 = yield readFile('/etc/shells');

console.log(f1.toString());

console.log(f2.toString());

};

使用 async 函数改写后如下,语法上看只是简单地把 * 换成 async,yield 替换成 await 而已。

const asyncReadFile = async function () {

const f1 = await readFile('/etc/fstab');

const f2 = await readFile('/etc/shells');

console.log(f1.toString());

console.log(f2.toString());

};

但 async 对 Generator 函数的改进体现在如下四点:

  • Generator 必须依靠内置执行器(co 模块)通过 next 语法执行,而 async 函数自带执行器执行起来像普通函数
  • 更好的语义,比起 * 和 yield,使用 async 和 await 语义更清楚
  • 更广的适用性,yield 命令后面只能是T runk 函数和 Promise 对象,而 async 的 await 后面可以是 Promisee 对象和原始值
  • async 函数的返回值是 Promise 对象,比起 Generator 函数的返回值是 Iteretor 方便,可以使用 then 方法指定下一步操作

5. Async/Await 的实际应用

未经过改写的代码

methods: {

handlePreview(file) {

this.dialogImageUrl = file.url;

this.dialogVisible = true;

},

queryAuditStatus(id){

G.R.Common.getAuditProgress('ACTIVITY_AUDIT', id).then(resp => {

if(resp.status == 0) {

this.processData = resp.data;

}

})

},

lookProduct(id){

this.$router.push({name:'prod_product_manage_view',params:{id:id}})

}

},

mounted() {

let params = this.$route.params || {};

this.loading = true;

G.R.Product.getActivityDetail(params.id).then(resp => {

console.log(resp)

if(resp.status == 0 && resp.data) {

this.product = Activity.parse(resp.data);

this.queryAuditStatus(params.id);

}else{

this.$message({

type:'info',

message:resp.message

})

}

this.loading = false;

})

let id = {

id:params.id

}

G.R.Product.getActivityOtherDispose(id).then(resp => {

console.log(21321321312312)

console.log(resp)

if(resp.status == 0 && resp.data) {

if(resp.data.length){

this.activityOther = resp.data;

}

}

this.loading = false;

})

}

使用 await 该改写后的代码:

  • 代码更少,可读性更好
  • 在 loading 的控制上更直接

  # /components/activity/view.vue

async mounted() {

let params = this.$route.params || {};

let id = params.id;

let activityRes = await G.R.Product.getActivityDetail(id);

let processRes = await G.R.Common.getAuditProgress('ACTIVITY_AUDIT', id);

let otherRes = await G.R.Product.getActivityOtherDispose({id});

this.loading = true;

if(activityRes.ok && processRes.ok && otherRes.ok) {

this.product = Activity.parse(activityRes.data);

this.processData = processRes.data;

if(otherRes.data && otherRes.data.length) {

this.activityOther = otherRes.data;

}

}

this.loading = false;

}

将三个请求改为并行缩短请求时间

  # /components/activity/view.vue

async mounted() {

...

let [activityRes, processRes, otherRes] = await Promise.all([

G.R.Product.getActivityDetail(id),

G.R.Common.getAuditProgress('ACTIVITY_AUDIT', id),

G.R.Product.getActivityOtherDispose({id})

])

...

}

以上是 Promise 和 Async / Await 的介绍 的全部内容, 来源链接: utcz.com/p/234383.html

回到顶部