Promise
JavaScript 是属于同步的程式语言,因此一次仅能做一件事情,但遇到非同步的事件时,就会将非同步的事件移动到程式码的最后方,等到所有的原始码运行完以后才会执行非同步的事件。
以下列的程式码来说,在 console 中依序的会出现的顺序为:
开始
结束
非同步事件 <- 最后执行
console.log('开始');setTimeout(() => { console.log('非同步事件');}, 0);console.log('结束');
虽然在上段的原始码中,setTimeout
所定义的时间为 0,但因为是属于非同步事件,因此还是会在其他原始码运行完以后才执行。
我们再axios
文件中找一段实际範例参考,先定义一个data物件
,中间段落使用 axios
尝试取得远端资料,后面的紧接的 console.log(data),实际测试看看console的结果是什么?
你应该会觉得concole顺序依序为:
"开始执行"
'/user?ID=12345
"测试有没有接收到资料"
let data = {}console.log('开始执行');const api = '/user?ID=12345'axios.get(api).then(response => { data = response; console.log('测试有没有接收到资料');});console.log(data);
其实正确的 console 中依序的会出现的顺序是:
"开始执行"
[object Object] { ... }
"测试有没有接收到资料"
你应该会想说为什么console.log(data)会没有接收到资料呢?
因为上述的非同步行为,导致api还尚未接收到,因此才会出现[object]
实战演练Promise,以Vue为範例
一、数字题型:
const app = new Vue({ el: '#app', created() { this.promise(100) .then(success => { console.log(success); return this.promise(1000); }) .then(success => { console.log(success); return this.promise(0); // 这个阶段会进入 catch }) .then(success => { // 由于上一个阶段结果是 reject,所以此段不执行 console.log(success); return this.promise('Hello'); }) .catch(error => { console.log(error); }) }, methods: { promise (num) { return new Promise((resolve, reject) => { num ? resolve('成功') : reject('失败'); }); } }, })
因此console出来的结果为:
成功
成功
失败
二、JSON Data一般题型:
将题型一的值更改为JSON,分别为apiA
以及apiB
可将此段程式码複製贴到开发者工具
看有没有什么不同的地方~
const app = new Vue({ el: '#app', created () { const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire' const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire' this.demo(apiA).then(res => { console.log(res) return this.demo(apiB) }).then(res => { console.log(res) }).catch(err => { console.log(err) }) }, methods: { demo (api) { return new Promise((resolve, reject) => { axios.get(api).then((res) => { const data = res.data.result.results res.status === 200 ? resolve(data) : reject('error api') }) }) } }})
三、进阶题型:
使用indexOf
去寻找哪个api中有符合80ab7634
的字串,有的话则会执行setTimeout
延迟5秒!
然而,在这个範例中indexOf
寻找到apiA
有符合,但是js是由上到下去执行的,因此js会等待apiA
回传回来后,接着马上跟着回传apiB
,因此apiA与apiB
都将延迟5秒回传
const app = new Vue({ el: '#app', created() { const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire' const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire' this.demo(apiA).then(res => { console.log(res); return this.demo(apiB) }).then(res => { console.log(res); }).catch(err => { console.log(err); }) }, methods: { demo(api) { return new Promise((resolve, reject) => { axios.get(api).then((res) => { if (res.status === 200) { if (api.indexOf('80ab7634') > -1) { setTimeout(() => { resolve(res.data) }, 5000) } else { resolve(res.data) } } else { reject('error api') } }) }) }, },})
假如我们将indexOf的值变更为36847f3f
,猜猜看会出现什么有趣的事呢?
这时会发现,js先回传apiA
,经过了5秒后才会回传apiB
因为js未搜寻到apiA有36847f3f
的值,因此不会去执行setTimeOut
const app = new Vue({ el: '#app', created() { const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire' const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire' this.demo(apiA).then(res => { console.log(res); return this.demo(apiB) }).then(res => { console.log(res); }).catch(err => { console.log(err); }) }, methods: { demo(api) { return new Promise((resolve, reject) => { axios.get(api).then((res) => { if (res.status === 200) { if (api.indexOf('36847f3f') > -1) { setTimeout(() => { resolve(res.data) }, 5000) } else { resolve(res.data) } } else { reject('error api') } }) }) }, },})
希望藉由上述三个不同的简单实例,能让大家更了解Promise的运作模式