前后端分离:异步与promise

182 阅读6分钟

刚开始时,什么意思都不明白,希望接下来能够理解的透彻一点。

JS异步编程模型

什么是同步

如果能直接拿到结果就是同步,比如你在医院挂号,拿到号才会离开窗口。同步任务有可能消耗10ms,也有可能需要3秒;

什么是异步

如果不能直接拿到结果 比如你在餐厅门口等位子,你拿到号可以去逛街。什么时候可以吃饭呢?

如何判断函数是同步还是异步

如果一个函数的返回值处于 setTimeout AJAX(即XMLHttpRequest) AddEventListener 这三个东西内部,那么这个函数就是异步函数 异步和Promise 什么是同步?什么是异步? 同步

直接能拿到结果,比如你去医院挂号,你拿到号才会离开窗口,同步任务可能消耗10ms,也有可能消耗3s,总之不拿到结果是不会离开的。

function f1() { console.log("你好"); } function f2() { console.log("hello"); } f1(); f2(); 复制代码 上面为同步代码,函数f2必须等f1执行完才能执行。

异步

如果不能直接拿到结果,比如你在餐厅门口等位置,你拿到号可以去逛街。

什么时候可以吃饭呢?

你可以每十分钟去餐厅询问(轮调)

你也可以用微信接收通知(回调)

比如下面代码:

function f1() { setTimeout(() => { console.log("你好"); }, 3000); } function f2() { console.log("hello"); } f1(); f2(); 复制代码 这个时候代码会直接先执行f2,等待最少3s才会执行函数f1,实际上是等待了3秒之后把f1放到了event queue里面,此时要等到主线程空闲的时候,才会执行event queue里面的f1函数。

js里面最基础的异步实现就是调用setTimeout,setInterval

回调(callback) 你写给自己的函数不是回调

你写给别人用的函数就是回调

比如request.onreadystatechange就是写给浏览器调用的

举个栗子:

function add(num1, num2, callback){ var sum = num1 + num2; callback(sum); }

function print(num){ console.log(num); }

add(1, 2, print); 复制代码 上面代码里就print是回调

异步和回调的关系 关联

异步任务需要的在得到结果时通知js来拿结果

让js留一个函数地址给浏览器

异步任务完成时浏览器调用该函数地址即可

同时把结果作为参数传给该函数

这个函数是我写给浏览器调用的,所以是回调函数

区别

异步任务需要用到回调函数来通知结果

但回调函数不一定只用在异步任务了,还可以用在同步任务里

array.forEach(n=>console.log(n))

判断同步还是异步

如果一个函数的返回值处于 setTimeout、ajax(即XMLHttpRequest)、AddEventListener这三个东西内部,那么这个函数就是异步函数。

Promise 如果异步任务有两个结果:成功和失败怎么办?

有两个方法:

1.回调接受两个参数

fs.readFile('./1.txt', (error, data)=>{ if(error){ console.log(' 失败'); return } console.log(data.toString()) //成功 }) 复制代码 2.搞两个回调

ajax('get','/1.json', data=>{}, error=>{}) //前面是成功回调,后面是失败回调 ajax('get', '/1.json', { success: ()=>{}, fail: ()=>{} }) // 接受一个对象,对象有两个Key,表示成功和失败 复制代码 但是不管是方法一还是方法二,都有问题:

1.代码不规范,名称五花八门

2.容易出现回调地狱

3.很难进行错误处理

怎么解决回调问题?

由此有人就提出了promise这种设计模式

它的用法:

return new Promise((resolve,reject)=>{...}) //任务成功时调用resolve(result) //任务失败时调用reject(error) resolve和reject会再调用成功和失败函数,它们只接收一个参数 使用.then(success,fail)传入成功和失败函数 复制代码 Promise介绍

Promise 不是前端发明的 Promise 是目前前端解决异步问题的统一方案 window.Promise 是一个全局函数,可以用来构造 Promise 对象 使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象,构造出来的 Promise 对象含有一个 .then() 函数属性

resolve 和 reject 并不是 .then(succes, fail) 里面的 success 和 fail,resolve 会去调用 success,reject 会去调用 fail .then

.then()方法返回一个Promise实例。它最多包含两个参数:用于的成功和失败情况的回调函数Promise。

使用.then方法

var p1 = new Promise((resolve, reject) => { resolve('Success!'); // or // reject(new Error("Error!")); });

p1.then(value => { console.log(value); // Success! }, reason => { console.error(reason); // Error! }); 复制代码 上面代码中,如果执行成功就会调用resolve(...), 当异步代码失败时就会调用reject(...),而 .then 中的函数参数就是上面调用resolve(...)方法传入的值。

getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // proceed }); 复制代码 上面的代码使用 then 方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。 如果前一个回调函数返回的是Promise对象,这时后一个回调函数就会等待该Promise对象有了运行结果,才会进一步调用。

.catch

用于指定发生错误时的回调函数。

getJSON("/posts.json").then(function(posts) { // some code }).catch(function(error) { // 处理前一个回调函数运行时发生的错误 console.log('发生错误!', error); }); 复制代码 Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。

getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 处理前两个回调函数的错误 }); 复制代码 Promise.all()

Promise.all(promiseArray)方法是Promise对象上的静态方法,该方法的作用是将多个Promise对象实例包装,生成并返回一个新的Promise实例。

参数:promiseArray,是一个Promise实例数组

var p1 = Promise.resolve(1), p2 = Promise.resolve(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { console.log(results); // [1, 2, 3] }); 复制代码 在上面的方法中,promise数组中所有的promise实例都变为resolve的时候,该方法才会返回,并将所有结果传递results数组中。promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。reject使用示例如下:

var p1 = Promise.resolve(1), p2 = Promise.reject(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { //then方法不会被执行 console.log(results); }).catch(function (e){ //catch方法将会被执行,输出结果为:2 console.log(e); }); 复制代码 Promise.race()

顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') },1000) })

let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') }, 500) })

Promise.race([p1, p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 打开的是 'failed' })