异步和Promise

287 阅读5分钟

什么是同步?什么是异步?

同步

直接能拿到结果,比如你去医院挂号,你拿到号才会离开窗口,同步任务可能消耗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'
})

前端小白,如有错误请留言指正!!!