zoukankan      html  css  js  c++  java
  • 理解js异步编程

    Promise

    1. 背景

      • javascript语言的一大特点就是单线程,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码,也就是说,同一个时间只能做一件事。
      • 怎么做到异步编程?回调函数。直到nodejs的出现,开始将回调模式的异步编程机制发挥的淋漓尽致,这种机制开始在前端变得非常流行,但是慢慢也体现出了回调函数在错误处理嵌套上的副作用。
      • 因为存在上面的不足,所以异步解决方案一直在发展中,从 callback => promise => generator => async/await => rx => .....
    2. 简单了解event loop

      +javascript上, 所有同步任务都在主线程上执行,也可以理解为存在一个“执行栈”。

      • 主线程外,还有一个“任务队列”,任务队列的作用,就在等待异步任务的结果,只要异步任务有了运行结果,就会加入到“任务队列”中。

      • 一旦执行栈中所有同步任务执行完毕,就从 任务队列 中读取“任务”加入到“执行栈”中。

      • 主线程不断的在循环上面的步骤。

        
        (function() {
        
          console.log('这是开始');
        
          setTimeout(function cb() {
            console.log('这是来自第一个回调的消息');
            setTimeout(function cb3() {
              console.log("这是来自第三个回调的消息");
            })
          });
        
          console.log('这是一条消息');
        
          setTimeout(function cb1() {
            console.log('这是来自第二个回调的消息');
            setTimeout(function cb3() {
              console.log("这是来自第四个回调的消息");
            })
          });
        
          console.log('这是结束');
        
        })();
        
        
    3. 什么是Promise

      • Promise代指那些尚未完成的一些操作,但是其在未来某个时间会返回某个特定的结果,成功或者失败。
      • 语法上来讲,promise是一个对象,代表一个未知的值,当值返回时,Promise总是处于下面的三种状态之一:
        • pending:等待。
        • resolved: 已完成。
        • rejected: 已拒绝。
      • 状态只可能从pening -> resolved | pending -> rejected,并且一旦改变,就不会再发生变化了。
      • promise对象必须是thenable的,而且then必须返回一个promise。
    4. 为什么要使用Promise

    • 代码看起来更符合逻辑,可读性更强。
    • 解决回调地狱
    • 更好的捕获错误
    1. 举几个栗子
    • 最简单的Promise

    这里看起来很简单,但有两点是要注意的

    1. 一定要resolve或者reject,否则你的then是永远也执行不到的。
    2. promise的状态一定改变后,就再也不会发生变化了。
      let promise = new Promise(function(resolve, reject) {
        resolve("success");
        reject("fail");
      });
     promise.then((value) => {
        console.log(value);
      }).catch((reason) => {
    	console.log(reason);
      });
    

    输出结果

    这个例子也充分证明了Promise只有一个状态结果,并且是不可变的

    18a20d2f9465c419476d87785685ee0f.png

    • 经典的回调地狱

      • 回调函数的写法

      多个异步事务多级依赖,回调函数会形成多级的嵌套,代码就会变成金字塔结构,不仅可读性不高,而且在后期的维护,调试或者重构上,都充满了风险。

    doSomething(function(result) {
      doSomethingElse(result, function(newResult) {
        doThirdThing(newResult, function(finalResult) {
          console.log('Got the final result: ' + finalResult);
        }, failureCallback);
      }, failureCallback);},
      failureCallback);
    
    • promise的写法

    解决了嵌套问题,thenable可以更好的支持链式调用,但还是能看到回调的影子
    更加简便的错误处理

    
    doSomething()
        .then(function(result) {
            return doSomethingElse(result);
        })
        .then(function(newResult) {
            return doThirdThing(newResult);
        })
        .then(function(finalResult) {
            console.log('Got the final result: ' + finalResult);
        })
        .catch(failureCallback);
    

    // 配合箭头函数

    
    doSomething()
        .then(result => doSomethingElse(result))
        .then(newResult => doThirdThing(newResult))
        .then(finalResult => {
        console.log(`Got the final result: ${finalResult}`);
        })
        .catch(failureCallback);
    
    
    • generator写法

    有一个暂停状态,只有我们激活next,才会去执行下一个异步任务

      function* fuc() {
              const result = yield doSomething()
              const newResult = yield doSomethingElse(result)
              const finalResult = yield doThirdThing(newResult)
        }
        const test = fuc()
        const result = test.next() // {value: 1, done: false}
        const newResult = test.next(result.value) // {value: 1, done: false}
        const finalResult = test.next(newResult.value) // {value: 1, done: true}
        test.next() // {value: undefined, done: true}
    
    • async/await的写法

    这个语法上更加简单,看起来更像同步任务,而且不需要关心执行状态。
    我理解这里只是对generator的一个包装,里面应该有个递归函数,在执行next,执行done。

    
    async function useAsyncAwait() {
        try {
            const result = await doSomething()
            const newResult = await doSomethingElse()
            const finalResult = await doThirdThing()
            console.log('Got the final result: ' + finalResult)
        } catch (e) {
            Console.error('exception: ', e)
        }
    }
    
    
    • 如何包装promise
     function getJSON(url) {
        return new Promise(function(resolve, reject){
          let xhr = new XMLHttpRequest();
          xhr.open('GET', url);
          xhr.onreadystatechange = handler;
          xhr.responseType = 'json';
          xhr.setRequestHeader('Accept', 'application/json');
          xhr.send();
          function handler() {
            if (this.readyState === this.DONE) {
              if (this.status === 200) {
                resolve(this.response);
              } else {
                reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
              }
            }
          };
        });
      }
      
      getJSON('/posts.json')
      .then((json) =>{
        // on fulfillment
      })
      .catch((error) => {
       // on rejection
       console.log(error)
      });
      
    
    • 多个promise顺序执行

    promise的then,是可以保证按照你看到的顺序执行的

      getJSON('/ray/api/a/server/ping')
      .then((json) =>{
        // on fulfillment
    	console.log('ping a result: ' + JSON.stringify(json));
      })
    .then(() => getJSON('/ray/api/b/server/ping'))
      .then(json => {
      	console.log('ping b result: ' + JSON.stringify(json)) 
      })
      .catch((error) => {
       // on rejection
       console.log(error)
      });
      
      // ping a result: {"status":"OK","serverTime":1573554742633}
     // ping b result: {"status":"OK","serverTime":1573554742667}
    
    • Promise 的链式调用

    举一个更具体的例子,体现thenable, promise的状态不可变

    
    var p = new Promise(function(resolve, reject){
      resolve(1);
    });
    p.then(function(value){               //第一个then
      console.log(value); // 1
      return value*2;
    }).then(function(value){              //第二个then
      console.log(value); // 2
    }).then(function(value){              //第三个then
      console.log(value); // underfined
      return Promise.resolve('resolve'); 
    }).then(function(value){              //第四个then
      console.log(value); // 'resolve'
      return Promise.reject('reject');
    }).then(function(value){              //第五个then
      console.log('resolve: '+ value); // 不到这里,没有值
    }, function(err){
      console.log('reject: ' + err);  // 'reject'
    })
    
    • 引用一些第三方库 比如 Bluebird

    比如
    mapSeries => 同步的执行所有异步任务,
    all => 等待并发的任务全部执行完毕,
    any => 多个异步任务中,有一个执行完毕就结束了。
    ==

    
    npm install bluebird
    
    import * as Promise from "bluebird";
    
    let data = ['a', 'c', 'b', 'e', 'd']
    Promise.mapSeries(data, (d) => getJSON(d) ).then((result) => {console.log(result)})
    
    // 执行结果应该是什么
    
    1. 如果没有修改代码 => 只执行了第一个a,后面的都应该第一个a出错,所以不继续执行了
    
    2. 如果将getJSON里的reject改成resoleve => a c b e d的出错log会按顺序打印
    
    
    • 多个promise并发执行
    
    Promise.all([ getJSON('/ray/api/a/server/ping'), getJSON('/ray/api/b/server/ping')]).then((result) => {
    	console.log('ping result: ' + JSON.stringify(result));
    })
    
    // ping result: [{"status":"OK","serverTime":1573554934072},{"status":"OK","serverTime":1573554934070}]
    
    
    • 多个promise之间的竞争

    promise 无法取消执行
    new Promise 里的任务会立即执行

    
    const delay = new Promise((resolve, reject) => { 
    	setTimeout(() => {
    		console.log('timeout');
    		resolve('timeout');
    	}, 3000)
    })
    
    Promise.race([ getJSON('/ray/api/a/server/ping'), delay]).then((result) => {
    	console.log('ping result: ' + JSON.stringify(result));
    })
    
    // 思考下这里的timeout会不会打印
    
    
    • Promise 的穿透

    promise 的then必须接收函数,否则会穿透。

    
    // 这里`Promise.resolve(2)`并不是函数,所以上一个函数的结果会穿透到下一个
    Promise.resolve(1).then(Promise.resolve(2)).then((v) => {
      console.log(v)
    })
    
    // 语法错误
    Promise.resolve(1).then(return Promise.resolve(2)).then((v) => {
      console.log(v)
    })
    
    // 穿透
    Promise.resolve(1).then(null).then((v) => {
      console.log(v)
    })
    
    // 语法错误
    Promise.resolve(1).then(return 2).then((v) => {
      console.log(v)
    })
    
    // then会返回新的promise,并且带上他返回的结果
    Promise.resolve(1).then(() => {
      return 2
    }).then((v) => {
      console.log(v)
    })
    
    

    当then()受非函数的参数时,会解释为then(null),这就导致前一个Promise的结果穿透到下面一个Promise。所以要提醒你自己:永远给then()传递一个函数参数。

    • 错误的捕获

    一旦捕获到错误,promise的then会继续执行
    catch会检查promis链上位于它之前的每个地方(then或者其他异步操作)。如果在它之前还有其他catch,那么起点就是上一个catch。

    var p1 = new Promise( function(resolve,reject){
      foo.bar();
      resolve( 1 );	  
    });
    
    p1.then(
      function(value){
        console.log('p1 then value: ' + value);
      },
      function(err){
        console.log('p1 then err: ' + err);
      }
    ).then(
      function(value){
        console.log('p1 then then value: '+value);
      },
      function(err){
        console.log('p1 then then err: ' + err);
      }
    );
    
    

    轻喷!

  • 相关阅读:
    Collection接口
    数组与集合
    Collection子接口:List接口
    第一篇博客
    在文件每行后边添加固定文本(shell)
    清理缓存
    cent7虚拟机切换root时出现"ABRT has detected ..."问题
    docker部署zabbix并设置自动发现规则
    python的u,r,b分别什么意思?
    docker镜像内没有vim
  • 原文地址:https://www.cnblogs.com/Sir-Lin/p/11868556.html
Copyright © 2011-2022 走看看