国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ECMAScript6(16):異步編程

曹金海 / 2588人閱讀

摘要:異步編程程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開始,此所謂同步。因此異步編程十分重要。

異步編程

程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開始,此所謂同步。如果程序在執(zhí)行一段代碼的同時可以去執(zhí)行另一段代碼,等到這段代碼執(zhí)行完畢再吧結(jié)果交給另一段代碼,此所謂異步。
比如我們需要請求一個網(wǎng)絡(luò)資源,由于網(wǎng)速比較慢,同步編程就意味著用戶必須等待下載處理結(jié)束才能繼續(xù)操作,所以用戶體驗(yàn)極為不好;如果采用異步,下載進(jìn)行中用戶繼續(xù)操作,當(dāng)下載結(jié)束了,告訴用戶下載的數(shù)據(jù),這樣體檢就提升了很多。因此異步編程十分重要。
從計算機(jī)的角度來講,js 只有一個線程,如果沒有異步編程那一定會卡死的!異步編程主要包括以下幾種:

回調(diào)函數(shù)

事件監(jiān)聽

發(fā)布/訂閱模型

Promise對象

ES6異步編程

回調(diào)函數(shù) 和 Promise

回調(diào)函數(shù)應(yīng)該是 js 中十分基礎(chǔ)和簡單的部分,我們在定義事件,在計時器等等使用過程中都使用過:

fs.readFile("/etc/passwd", function(err, data){
  if(err) throw err;
  console.log(data);
});

比如這里的這個文件讀取,定義了一個回調(diào)函數(shù),在讀取文件成功或失敗是調(diào)用,并不會立刻調(diào)用。

如同之前在 Promise 中提到的,當(dāng)我想不斷的讀入多個文件,就會遇到回調(diào)函數(shù)嵌套,書寫代碼及其的不方便,我們稱之為"回調(diào)地獄"。因此 ES6 中引入是了 Promise 解決這個問題。具體表現(xiàn)參看之前的 Promise 部分。但是 Promise 也帶來了新的問題,就是代碼冗余很嚴(yán)重,一大堆的 then 使得回調(diào)的語義不明確。

協(xié)程

所謂協(xié)程就是幾個程序交替執(zhí)行:A開始執(zhí)行,執(zhí)行一段時間后 B 執(zhí)行,執(zhí)行一段時間后再 A 繼續(xù)執(zhí)行,如此反復(fù)。

function* asyncJob(){
  //...
  var f = yield readFile(fileA);
  //...
}

通過一個 Generator 函數(shù)的 yield, 可以將一個協(xié)程中斷,去執(zhí)行另一個協(xié)程。我們可以換一個角度理解 Generator 函數(shù):它是協(xié)程在 ES6 中的具體體現(xiàn)。我們可以簡單寫一個異步任務(wù)的封裝:

var fetch = require("node-fetch");
function* gen(){
  var url = "http://api.github.com/users/github";
  var result = yield fetch(url);
  console.log(result.bio);
}

var g = gen();
var result = g.next();    //返回的 value 是一個 Promise 對象
result.value.then(function(data){
  return data.json;
}).then(function(data){
  g.next(data);
});
Thunk 函數(shù)

在函數(shù)傳參數(shù)時我們考慮這樣一個問題:

function fun(x){
  return x + 5;
}
var a = 10;
fun(a + 10);

這個函數(shù)返回25肯定沒錯,但是,我們傳給函數(shù) fun 的參數(shù)在編譯時到底保留 a + 10 還是直接傳入 20?顯然前者沒有事先計算,如果函數(shù)內(nèi)多次使用這個參數(shù),就會產(chǎn)生多次計算,影響性能;而后者事先計算了,但如果函數(shù)里不使用這個變量就白浪費(fèi)了性能。采用把參數(shù)原封不動的放入一個函數(shù)(我們將這個函數(shù)稱為 Thunk 函數(shù)),用的使用調(diào)用該函數(shù)的方式。也就是上面的前一種方式傳值。所以上面代碼等價于:

function fun(x){
  return x() + 5;
}
var a = 10;
var thunk = function(){ return a + 10};
fun(thunk);

但是 js 不是這樣的!js 會把多參數(shù)函數(shù)給 Thunk 了,以減少參數(shù):

var fs = require("fs");
fs.readFile(fileName, callback);
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function(fileName){
  return function(callback){
    return fs.readFile(fileName,callback);
  };
};

這里任何具有回調(diào)函數(shù)的函數(shù)都可以寫成這樣的 Thunk 函數(shù),方法如下:

function Thunk(fn){
  return function(){
    var args = Array.prototype.slice.call(arguments);
    return function (callback){
      args.push(callback);
      return fn.apply(this, args);
    }
  }
}

//這樣fs.readFile(fileName, callback); 寫作如下形式

Thunk(fs.readFile)(fileName)(callback);

關(guān)于 Thunk 函數(shù), 可以直接使用 thunkify 模塊:

npm install thunkify

使用格式和上面的Thunk(fs.readFile)(fileName)(callback);一致,但使用過程中需要注意,其內(nèi)部加入了檢查機(jī)制,只允許 callback 被回調(diào)一次!

結(jié)合 Thunk 函數(shù)和協(xié)程,我們可以實(shí)現(xiàn)自動流程管理。之前我們使用 Generator 時候使用 yield 關(guān)鍵字將 cpu 資源釋放,執(zhí)行移出 Generator 函數(shù)。可以怎么移回來呢?之前我們手動調(diào)用 Generator 返回的迭代器的 next() 方法,可這畢竟是手動的,現(xiàn)在我們就利用 Thunk 函數(shù)實(shí)現(xiàn)一個自動的:

var fs = require("fs");
var thunkify = require("thunkify");
var readFile = thunkify(fs.readFile);

var gen = function*(...args){    //args 是文件路徑數(shù)組
  for(var i = 0, len = args.length; i < len; i++){
    var r = yield readFile(args[i]);
    console.log(r.toString());
  }
};

(function run(fn){
  var gen = fn();
  function next(err, data){
    if(err) throw err;
    var result =  gen.next(data);
    if(result.done) return;    //遞歸直到所以文件讀取完成
    result.value(next);    //遞歸執(zhí)行
  }
  next();
})(gen);

//之后可以使用 run 函數(shù)繼續(xù)讀取其他文件操作

如果說 Thunk 可以有現(xiàn)成的庫使用,那么這個自動執(zhí)行的 Generator 函數(shù)也有現(xiàn)成的庫可以使用——co模塊(https://github.com/tj/co)。用法與上面類似,不過 co 模塊返回一個 Promise 對象。使用方式如下:

var co = require("co");
var fs = require("fs");
var thunkify = require("thunkify");
var readFile = thunkify(fs.readFile);

var gen = function*(...args){    //args 是文件路徑數(shù)組
  for(var i = 0, len = args.length; i < len; i++){
    var r = yield readFile(args[i]);
    console.log(r.toString());
  }
};
co(gen).then(function(){
  console.log("files loaded");
}).catch(function(err){
  console.log("load fail");
});

這里需要注意的是:yield 后面只能跟一個 thunk 函數(shù)或 promise 對象。上例中第8行 yield 后面的 readFile 是一個 thunk 函數(shù),所以可以使用。
上面已經(jīng)講解了 thunk 函數(shù)實(shí)現(xiàn)自動流程管理,下面使用 Promise 實(shí)現(xiàn)一下:

var fs = require("fs");
var readFile = function(fileName){
  return new Promise(function(resolve, reject){
    fs.readFile(fileName, function(error,data){
      if(error) reject(error);
      resolve(data);
    });
  });
};

var gen = function*(){
  for(var i = 0, len = args.length; i < len; i++){
    var r = yield readFile(args[i]);
    console.log(r.toString());
  }
};

(function run(gen){
  var g = gen();

  var resolve = function(data){
    var result = g.next(data);
    if(result.done) return result.value;
    result.value.then(resolve);
  }
  g.next().value.then(function(data){
    resolve(data);
  });
  resolve();
})(gen);
//之后可以使用 run 函數(shù)繼續(xù)讀取其他文件操作
async 函數(shù)

ES7 中提出了 async 函數(shù),但是現(xiàn)在已經(jīng)可以用了!可這個又是什么呢?其實(shí)就是 Generator 函數(shù)的改進(jìn),我們上文寫過一個這樣的 Generator 函數(shù):

var gen = function*(){
  for(var i = 0, len = args.length; i < len; i++){
    var r = yield readFile(args[i]);
    console.log(r.toString());
  }
};

我們把它改寫成 async 函數(shù):

var asyncReadFiles = async function(){    //* 替換為 async
  for(var i = 0, len = args.length; i < len; i++){
    var r = await readFile(args[i]);   //yield 替換為 await
    console.log(r.toString());
  }
};

async 函數(shù)對 Generator 函數(shù)做了一下改進(jìn):

Generator 函數(shù)需要手動通過返回值的 next 方法執(zhí)行,而 async 函數(shù)自帶執(zhí)行器,執(zhí)行方式和普通函數(shù)完全一樣。

var result = asyncReadFiles(fileA, fileB, fileC);

語義明確,async 表示異步,await 表示后續(xù)表達(dá)式需要等待觸發(fā)的異步操作結(jié)束

co 模塊中 yield 后面只能跟一個 thunk 函數(shù)或 promise 對象,而 await 后面可以是任何類型(不是 Promise 對象就同步執(zhí)行)

返回值是一個 Promise 對象,不是 Iterator ,比 Generator 方便

我們可以實(shí)現(xiàn)這樣的一個 async 函數(shù):

async function asyncFun(){
  //code here
}
//equal to...
function asyncFun(args){
  return fun(function*(){
    //code here...
  });
  function fun(genF){
    return new Promise(function(resolve, reject){
      var gen = genF();
      function step(nextF){
        try{
          var next = nextF();
        } catch(e) {
          return reject(e);
        }
        if(next.done){
          return resolve(next.value);
        }
        Promise.resolve(next.value).then(function(data){
          step(function(){ return gen.next(data); });
        }, function(e){
          step(function(){ return gen.throw(e); });
        });
      }
      step(function() { return gen.next(undefined); });
    });
  }
}

我們使用 async 函數(shù)做點(diǎn)簡單的事情:

function timeout(ms){
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function delay(nap, ...values){
  while(1){
    try{
      await timeout(nap);
    } catch(e) {
      console.log(e);
    }
    var val = values.shift();
    if(val)
      console.log(val)
    else
      break;
  }
}
delay(600,1,2,3,4);   //每隔 600ms 輸出一個數(shù)

這里需要注意:應(yīng)該把后面跟 promise對象的 await 放在一個 try 中,防止其被 rejected。當(dāng)然上面的 try 語句也可以這樣寫:

var ms = await timeout(nap).catch((e) => console.log(e));

對于函數(shù)參數(shù)中的回調(diào)函數(shù)不建議使用,避免出現(xiàn)不應(yīng)該的錯誤

//反例: 會得到錯誤結(jié)果
async function fun(db){
  let docs = [{},{},{}];

  docs.forEach(async function(doc){   //ReferenceError: Invalid left-hand side in assignment
    await db.post(doc);
  });
}

//改寫, 但依然順序執(zhí)行
async function fun(db){
  let docs = [{},{},{}];

  for(let doc of docs){
    await db.post(doc);
  }
}

//改寫, 并發(fā)執(zhí)行
async function fun(db){
  let docs = [{},{},{}];
  let promises = docs.map((doc) => db.post(doc));
  let result = await Promise.all(promises)
  console.log(result);
}

//改寫, 并發(fā)執(zhí)行
async function fun(db){
  let docs = [{},{},{}];
  let promises = docs.map((doc) => db.post(doc));
  let result = [];
  for(let promise of promises){
    result.push(await promise);
  }
  console.log(result);
}
Promise,Generator 和 async 函數(shù)比較

這里我們實(shí)現(xiàn)一個簡單的功能,可以直觀的比較一下。實(shí)現(xiàn)如下功能:

在一個 DOM 元素上綁定一系列動畫,每一個動畫完成才開始下一個,如果某個動畫執(zhí)行失敗,返回最后一個執(zhí)行成功的動畫的返回值

Promise 方法

function chainAnimationPromise(ele, animations){
  var ret = null;  //存放上一個動畫的返回值
  var p = Promise.resolve();
  for(let anim of animations){
    p = p.then(function(val){
      ret = val;
      return anim(ele);
    });
  }
  return p.catch(function(e){
    /*忽略錯誤*/
  }).then(function(){
    return ret;  //返回最后一個執(zhí)行成功的動畫的返回值
  });
}

Generator 方法

function chainAnimationGenerator(ele, animations){
  return fun(function*(){
    var ret = null;
    try{
      for(let anim of animations){
        ret = yield anim(ele);
      }
    } catch(e) {
      /*忽略錯誤*/
    }
    return ret;
  });

  function fun(genF){
    return new Promise(function(resolve, reject){
      var gen = genF();
      function step(nextF){
        try{
          var next = nextF();
        } catch(e) {
          return reject(e);
        }
        if(next.done){
          return resolve(next.value);
        }
        Promise.resolve(next.value).then(function(data){
          step(function(){ return gen.next(data); });
        }, function(e){
          step(function(){ return gen.throw(e); });
        });
      }
      step(function() { return gen.next(undefined); });
    });
  }
}

async 函數(shù)方法

async function chainAnimationAsync(ele, animations){
  var ret = null;
  try{
    for(let anim of animations){
      ret = await anim(elem);
    }
  } catch(e){
    /*忽略錯誤*/
  }
  return ret;
}
一個經(jīng)典題
console.log(0);

setTimeout(function(){
  console.log(1)
},0);
setTimeout(function(){
  console.log(2);
},1000);

var pro = new Promise(function(resolve, reject){
  console.log(3);
  resolve();
}).then(resolve => console.log(4));

console.log(5);

setTimeout(function(){
  console.log(6)
},0);

pro.then(resolve => console.log(7));

var pro2 = new Promise(function(resolve, reject){
  console.log(8);
  resolve(10);
}).then(resolve => console.log(11))
  .then(resolve => console.log(12))
  .then(resolve => console.log(13));

console.log(14);

// 0 3 5 8 14 4 11 7 12 13 1 6 2

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97441.html

相關(guān)文章

  • ECMAScript6(13):Generator 函數(shù)

    摘要:函數(shù)可以沒有返回值,此時它依然返回一個并且在調(diào)用方法時一次行執(zhí)行完函數(shù)內(nèi)全部代碼,返回。將一個可遍歷結(jié)構(gòu)解構(gòu),并逐一返回其中的數(shù)據(jù)。 Generator Generator 函數(shù)是 es6 中的新的異步編程解決方案,本節(jié)僅討論 Generator 函數(shù)本身,異步編程放在后面的部分。Generator 函數(shù)之前也提到過,描述內(nèi)部封裝的多個狀態(tài),類似一個狀態(tài)機(jī),當(dāng)然也是很好的 iterat...

    wangxinarhat 評論0 收藏0
  • 這么多前端優(yōu)化點(diǎn)你都記得住嗎?

    摘要:不推薦移動端瀏覽器前端優(yōu)化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點(diǎn)設(shè)備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應(yīng)用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優(yōu)化策略進(jìn)行羅列注意,是羅列不是展開,遇到不會不懂的點(diǎn)還請站外擴(kuò)展 開車...

    ysl_unh 評論0 收藏0
  • 這么多前端優(yōu)化點(diǎn)你都記得住嗎?

    摘要:不推薦移動端瀏覽器前端優(yōu)化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點(diǎn)設(shè)備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應(yīng)用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優(yōu)化策略進(jìn)行羅列注意,是羅列不是展開,遇到不會不懂的點(diǎn)還請站外擴(kuò)展 開車...

    Tecode 評論0 收藏0
  • 這么多前端優(yōu)化點(diǎn)你都記得住嗎?

    摘要:不推薦移動端瀏覽器前端優(yōu)化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點(diǎn)設(shè)備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應(yīng)用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優(yōu)化策略進(jìn)行羅列注意,是羅列不是展開,遇到不會不懂的點(diǎn)還請站外擴(kuò)展 開車...

    Tonny 評論0 收藏0
  • Javascript的異步編程:Promise

    摘要:對象是工作組為異步編程提供的統(tǒng)一接口,是中提供了對的原生支持,就是在未來發(fā)生的事情,使用可以避免回調(diào)函數(shù)的層層嵌套,還提供了規(guī)范更加容易的對異步操作進(jìn)行控制。是執(zhí)行完之后的回調(diào),可以用方法分別指定和的回調(diào)。 Promise對象是CommonJS工作組為異步編程提供的統(tǒng)一接口,是ECMAScript6中提供了對Promise的原生支持,Promise就是在未來發(fā)生的事情,使用Promis...

    forsigner 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<