异步同步的简单解析(一)之异步问题三种解决方式

448 阅读6分钟

主要内容

  • 同步和异步的概念
  • 用方块运动表示同步和异步

同步异步概念

同步和异步是一种消息通知机制,在js中一般表现都是,同步阻塞,异步非阻塞

  • 同步阻塞: A调用B,B处理获得结果,才返回给A。A在这个过程中,一直等待B的处理结果,没有拿到结果之前,需要A(调用者)一直等待和确认调用结果是否返回,拿到结果,然后继续往下执行。做一件事,没有拿到结果之前,就一直在这等着,一直等到有结果了,再去做下边的事

  • 异步非阻塞: A调用B,无需等待B的结果,B通过状态,通知等来通知A或回调函数来处理。做一件事,不用等待事情的结果,然后就去忙别的了,有了结果,再通过状态来告诉我,或者通过回调函数来处理。

  • 举个例子

    小明去借书,想要一本node.js书。图书管理员就去找。同步和异步是针对图书管理员的。如果图书管理员找到或者没有找到会有一个结果通知小红这就是异步。如果什么结果都没有只是去找,就是简单理解为同步。阻塞和非阻塞是对小明来说,如果管理员找书这个时间,小明一直在等待,那么就是阻塞。如果小明这段时间去做别的事,等管理员结果,那么就事非阻塞。

JS单线程,主线程,异步线程

刚开始设计之初js都是单线程。JS会把同步任务放在主线程队列里面,遇到的异步任务会放到异步线程对了。会把同步任务的主线程执行完成后才执行异步线程。任务会在队列中依次执行。这个行为叫做EVENT-LOOP

sTzmHf.png

JS单线程,主线程,异步线程 顺序

	//异步函数2
    function asFn2() {
        setTimeout(function () {
            console.log('fn2')
        })
    }

    console.log(111)
	as()
	function as() {
        console.log(222);
    }
    asFn1();
    asFn2();
    //异步函数1
    function asFn1() {
		setTimeout(function () {
            console.log('fn1')
        })
    }
    console.log(333)

//result
111
222
333
fn1
fn2

异步问题--回调函数

有很多时候,我们希望异步执行完成后,再执行我们想要的逻辑。我们可以考虑把逻辑写在异步函数里面,让异步函数执行完成后执行我们的逻辑。但是很多库是不对外暴露。我们不能写在里面,也不方便解耦,所以我们可以利用回调函数。将函数当参数传入。

  • 例子

    	function asnycFn(cb) {
    		setTimeout(function () {
                console.log('asnycFn...');
                // console.log('myFn...');//逻辑写在异步中
    			cb && cb(); //callback 函数
            })
        }
    
        function myFn() {
            console.log('myFn...')
        }
        asnycFn(myFn);
    	
    	//result
    	/*
    	* asnycFn...
    	* myFn...
    	* 
    	* */
    

异步问题--ES6 promise

ES6为了避免回调地狱,推出了promise。可以让用户可以链式操作。

ES6的Promise对象是一个构造函数,用来生成Promise实例。所谓Promise对象,就是代表了未来某个将要发生的事件(通常是一个异步操作)。

  • promise 三种状态
    • Promise对象的三种状态 pending 、resolve 和 reject
    • pending
      	let p1 = new Promise(((resolve, reject) => {}))
      
          console.log(p1);
      
      //
      

      s7Z9Ej.png

    • resolve == fulfilled
      	let p1 = new Promise(((resolve, reject) => {
      	    resolve('resolve...');
      	}))
      
          console.log(p1);
      

      s7ZJKO.png

    • reject
      let p1 = new Promise(((resolve, reject) => {
             reject('reject...');
      }))
      
         console.log(p1);
      

      s7ZdIA.png

  • then 的两个参数
    • then的2个参数;onresolved 和 onrejected;
      	let p1 = new Promise((resolve, reject) => {
              resolve('resolve...');
      	}).then((onresolved,onrejected)=>{
              console.log(onresolved);
      	})
      // 拿到的是 resolve 状态下的value 值  resolve...
      
         
      
      	let p1 = new Promise((resolve, reject) => {
              reject('reject...');
      	}).then((onresolved)=>{
      
      	},(onrejected)=>{
              console.log(onrejected);
      
      	})
      	
      // 拿到的是 reject 状态下的value 值  reject...
      
  • then 的三种返回值
    • then的返回值,会返回一个新的 Promise 对象。这决定了then 之后还能then。实现了链式操作

    • then 的回调函数中没有返回值,then就会返回一个状态为: resolved 的 promise 对象

      	let p1 = new Promise((resolve, reject) => {
              resolve('reject...');
      	}).then((res)=>{
      
      	},(rej)=>{
              // console.log(onrejected);
      
      	})
      
           console.log(p1);
      
    • then 的回调函数返回值是 非 promise 的值, then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 then

      	let p1 = new Promise((resolve, reject) => {
              resolve('reject...');
      	}).then((res)=>{
      		return 111
      	},(rej)=>{
              // console.log(onrejected);
      
      	})
      
           console.log(p1);
      

      s7efmD.png

    • then 的回调函数返回值是 promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义。

      	let p1 = new Promise((resolve, reject) => {
              resolve('resolve...');
      	}).then((res)=>{
      	    //resolve(res) res 可以是其他自己自定义的值
      		return new Promise(resolve => resolve(res));
      	},(rej)=>{
              // console.log(onrejected);
      
      	})
      
           console.log(p1);
      

      s7mV74.png

  • Promise 静态方法

    Promise 下的方法:resolve、reject、all、race、finally

    • all 传入一个Promise数组 ,返回一个数组结果

      **注意:**要所有结果都是成功的就返回

      const p1 = new Promise((resolve, reject) => {
          setTimeout(()=>{
                 console.log('111')
         })
      })
      
         const p2 = new Promise((resolve, reject) => {
             setTimeout(()=>{
                 console.log('222')
             })
         })
      
      let result = Promise.all([p1,p2]);
         console.log('result',result);
      
      
      //111
      //222
      
    • race

      那个快返回那个。上面的案例中 就会返回一个 222的值

    • finally

      无论成功还是失败,都会执行。都会执行一次

异步问题--ES7 Async await 函数

用同步代码写出异步。基于promise 改写。await 依赖 Async 。await 后面要跟 promise 对象。错误使用 try catch 块捕抓

	const p1 = function mp1() {
		return new Promise((resolve, reject) => {
            setTimeout(()=>{
              resolve('111')
            },1000)
        })
    }


    const p2 = function mp2() {
        return new Promise((resolve, reject) => {
            setTimeout(()=>{
                resolve('222')
            },1000)
        })
    }

	async function fn() {
		try{
		    let resp1 = await p1();
            console.log(resp1)
			let resp2= await p2();
            console.log(resp2);

		}
		catch (e) {
            console.log(e)
        }
    }

    fn();

//111
//222

案例:方块运动

回调函数执行。

需求是让方块先往右,再向下,再往左,再向上执行

s7AD2T.png

那么就可以使用回调函数执行,但是很容造成回调地狱

	const box = document.querySelector('.box');


	function move(ele,target,direction,cb) {
	    //转成number 结果带px
		let start = parseInt(window.getComputedStyle(ele,null)[direction]);

		//利用 1 -1 控制方向
		let dirArrow= (target - start) / Math.abs((target - start));
		let speed = 5 * dirArrow;
        start += speed;
        console.log(start);
		//如果 start 为 300 target 为 0
		//start > = target 条件马上成立,就不会执行回调
		// 可以让target start 绝对值 进行对比, start 不停减少当两者绝对值小于speed 证明运动完成了
        console.log(Math.abs(target - start));
		if(Math.abs(target - start) <= 5){
			cb && cb();
		}
		else {
		    setTimeout(()=>{
                box.style[direction] = start + 'px';
                move(ele,target,direction,cb);
			},20);

		}
    }

    move(box,300,'left',function () {
		move(box,300,'top',function () {
           move(box,0,'left',function () {
               move(box,0,'top',function () {
                   console.log('run over');
               })
           })
        })
    });
	/*
	* 这样会同步执行,他不先执行move(box,300,'left'); 再执行move(box,300,'top');
	*
	* move(box,300,'left');
	* move(box,300,'top');
	*
	* 这时候就需要回调
	*
	* */

Promise 改造

由于它里面右递归,所以不能用move 回调。要封装一个run 函数。保证每次返回一promise

const box = document.querySelector('.box');


 function move(ele,target,direction) {

    return new Promise((resolve) => {
           function run() {
               //转成number 结果带px
               let start = parseInt(window.getComputedStyle(ele,null)[direction]);
               //利用 1 -1 控制方向
               let dirArrow= (target - start) / Math.abs((target - start));
               let speed = 5 * dirArrow;
               start += speed;
               //如果 start 为 300 target 为 0
               //start > = target 条件马上成立,就不会执行回调
               // 可以让target start 绝对值 进行对比, start 不停减少当两者绝对值小于speed 证明运动完成了
               if(Math.abs(target - start) <= 5){
                   resolve('run over ...');
               }
               else {
                   setTimeout(()=>{
                       box.style[direction] = start + 'px';
                       //这里会有递归 
                       run();
                   },20);

               }
           }

           run();
   })


   }

   move(box,300,'left').then(res=>{
       return move(box,300,'top')
   }).then(res=>{
           return move(box,0,'left')
       }).then(res=>{
           return move(box,0,'top')
       }).then(res=>{
       console.log('run over..')
}).catch(rej =>{
       console.log('err--',rej)
})

Async await 改造

写法更加简洁。

    async function rectRun() {
		try {
            await move(box,300,'left');
            await move(box,300,'top');
            await move(box,0,'left');
           let reS =  await move(box,0,'top');
            console.log(reS);

        }catch (e) {
            console.log(e)
        }
    }

    rectRun();

全部代码

gitee传送门