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

資訊專欄INFORMATION COLUMN

Javascript關(guān)于異步編程的發(fā)展

princekin / 1880人閱讀

摘要:前言轉(zhuǎn)簡(jiǎn)體重新排版布局代碼全面使用并且直接附上輸出結(jié)果補(bǔ)充細(xì)節(jié)補(bǔ)充內(nèi)容增加例子說(shuō)明新增和在遍歷情況下怎么使用上文講了關(guān)于執(zhí)行機(jī)制單線程同異步任務(wù)事件循環(huán)的知識(shí)點(diǎn)我們知道在某一時(shí)刻內(nèi)只能執(zhí)行特定的一個(gè)任務(wù)并且會(huì)阻塞其它任務(wù)執(zhí)行為了解決這個(gè)

前言

PS:
2018/08/08 轉(zhuǎn)簡(jiǎn)體
2018/08/09 重新排版布局,代碼全面使用ES6并且直接附上輸出結(jié)果,補(bǔ)充細(xì)節(jié)
2018/08/13 補(bǔ)充Async..await內(nèi)容,增加例子說(shuō)明
2018/08/20 新增Async..await和Promise.all在遍歷情況下怎么使用

上文講了關(guān)于Javascript執(zhí)行機(jī)制--單線程,同異步任務(wù),事件循環(huán)的知識(shí)點(diǎn),我們知道Javascript在某一時(shí)刻內(nèi)只能執(zhí)行特定的一個(gè)任務(wù),并且會(huì)阻塞其它任務(wù)執(zhí)行,為了解決這個(gè)問(wèn)題,Javascript語(yǔ)言將任務(wù)的執(zhí)行模式分成兩種:

同步(Synchronous):一定要等任務(wù)執(zhí)行完了,得到結(jié)果,才執(zhí)行下一個(gè)任務(wù);

異步(Asynchronous):不等任務(wù)執(zhí)行完,直接執(zhí)行下一個(gè)任務(wù),等到后面再繼續(xù)執(zhí)行未完成的任務(wù);

現(xiàn)在我們就講講關(guān)于異步編程的發(fā)展
更多細(xì)節(jié)請(qǐng)看阮一峰大神的《ECMAScript 6 入門(mén)》

總結(jié)圖

如果看不清晰右鍵圖片新標(biāo)簽頁(yè)打開(kāi)

回調(diào)函數(shù) 百度百科的解釋是:
回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù).如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù).回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng).
機(jī)制:
⑴定義一個(gè)回調(diào)函數(shù);
⑵提供函數(shù)實(shí)現(xiàn)的一方在初始化的時(shí)候,將回調(diào)函數(shù)的函數(shù)指針注冊(cè)給調(diào)用者;
⑶當(dāng)特定的事件或條件發(fā)生的時(shí)候,調(diào)用者使用函數(shù)指針調(diào)用回調(diào)函數(shù)對(duì)事件進(jìn)行處理.

例如:

//定義一個(gè)回調(diào)函數(shù)
function callback() {
  console.log("I am a callback!");
}

//函數(shù)實(shí)現(xiàn)
function trigger(fn, time) {
  setTimeout(function() {
    fn && fn();
  }, time || 1000);
}

//耐心等兩秒哦
trigger(callback, 2000);

//輸出
//I am a callback!
優(yōu)劣點(diǎn) 缺點(diǎn):

線性理解能力缺失,代碼高度耦合,不利于維護(hù)閱讀,嵌套層級(jí)深的情況流程混亂,陷入“回調(diào)地獄”;

因?yàn)榛卣{(diào)本身錯(cuò)誤無(wú)法被外層代碼捕捉,需要在實(shí)現(xiàn)內(nèi)部里實(shí)現(xiàn)額外的容錯(cuò)代碼(返回報(bào)錯(cuò),多次執(zhí)行,不執(zhí)行等);

tips:因?yàn)楫惒饺蝿?wù)在執(zhí)行機(jī)制里處理方式不同的問(wèn)題,try/catch語(yǔ)句只能捕捉執(zhí)行棧上的錯(cuò)誤,詳情請(qǐng)回顧Javascript執(zhí)行機(jī)制--單線程,同異步任務(wù),事件循環(huán)

事件監(jiān)聽(tīng)

也叫觀察者模式,這是一種常見(jiàn)的編程方式,一個(gè)對(duì)象(目標(biāo)對(duì)象和觀察者對(duì)象)的狀態(tài)發(fā)生改變,所有的依賴對(duì)象(觀察者對(duì)象)都將得到通知,進(jìn)行廣播通知.執(zhí)行事件由觸發(fā)事件調(diào)用與順序無(wú)關(guān),易用低耦合并且不依賴函數(shù)調(diào)用.

DOM監(jiān)聽(tīng)事件



  
    
    
  

  
    
  • item1
  • item2
  • item3
優(yōu)劣點(diǎn) 優(yōu)點(diǎn):

低耦合,目標(biāo)對(duì)象和觀察者對(duì)象建立了一個(gè)抽象的關(guān)系;

一對(duì)多的映射關(guān)系,目標(biāo)對(duì)象的狀態(tài)發(fā)生改變,所有的觀察者對(duì)象都將得到通知,進(jìn)行各自的回調(diào)操作;

任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個(gè)事件是否發(fā)生;

缺點(diǎn):

盡管松耦合的通信方式優(yōu)于對(duì)象之間的硬編碼,代碼流程依然是硬傷,因?yàn)槟憧赡懿恢涝谀睦锒x了什么回調(diào)函數(shù)觸發(fā)了什么事件發(fā)生;

發(fā)布/訂閱

這是一種類似事件監(jiān)聽(tīng)但是卻更強(qiáng)大的設(shè)計(jì)模式(很多人也把兩者視為同一種模式,個(gè)人覺(jué)得還是有點(diǎn)區(qū)別的),例如:

前者由目標(biāo)對(duì)象負(fù)責(zé)調(diào)度,后者由一個(gè)調(diào)度中心負(fù)責(zé)調(diào)度;

前者低耦合,后者不耦合;

例如我們可以自己封裝一個(gè)方法以實(shí)現(xiàn)更多監(jiān)聽(tīng)事件

var listener = (function() {
  //訂閱隊(duì)列
  var list = [],
    //監(jiān)聽(tīng)事件
    on = function(type, fn) {
      if (!list[type]) list[type] = [];
      list[type].push(fn);
    },
    //取消監(jiān)聽(tīng)事件
    off = function(type, fn) {
      list[type] = [];
    },
    //觸發(fā)事件
    trigger = function() {
      //取出監(jiān)聽(tīng)類型
      var type = Array.prototype.shift.call(arguments),
        queue = list[type],
        i = 0,
        len = queue.length;

      for (; i < len; i++) {
        //帶參數(shù)發(fā)布
        queue[i].apply(this, arguments);
      }
    };

  return {
    on: on,
    off: off,
    trigger: trigger,
  };
})();

listener.on("log", function() {
  console.log("I trigger a log!");
});
listener.trigger("log");

//取消之后不觸發(fā)
listener.off("log");
listener.trigger("log");

//輸出
//I trigger a log!
優(yōu)劣點(diǎn) 優(yōu)點(diǎn):

調(diào)度中心統(tǒng)一管理目標(biāo)對(duì)象和觀察者對(duì)象相關(guān)信息,之間互不依賴;

Promise

上面的方法不管怎么變化,始終避不開(kāi)回調(diào)地獄的困境,直到后來(lái)被社區(qū)提出和實(shí)現(xiàn)并且已經(jīng)被放置到ES6統(tǒng)一標(biāo)準(zhǔn)化的---Promise出現(xiàn).
簡(jiǎn)單來(lái)說(shuō)就兩個(gè)特性:

初始化時(shí)候的pending(進(jìn)行中)狀態(tài)只能通過(guò)異步操作的結(jié)果更改成fulfilled/resolved(已成功)或者rejected(已失敗)的狀態(tài),即其他任何手段都不能影響到;

狀態(tài)一旦更改是不可逆,即使之后再添加回調(diào)函數(shù)也只是返回同個(gè)結(jié)果,這個(gè)特點(diǎn)恰恰能解決上面說(shuō)的回調(diào)函數(shù)無(wú)法被外層代碼捕捉錯(cuò)誤的缺點(diǎn);

const promise = new Promise((resolve, reject) => {
  true ? resolve("success") : reject("error");
});
//Promise實(shí)例
console.log(promise);

promise.then(
  function resolved(res) {
    console.log(res);
  },
  function rejected(err) {
    console.log(err);
  }
);

//輸出
//Promise {  }
//success

Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),JavaScript 引擎提供兩個(gè)參數(shù)分別是:

參數(shù)名 用途
resolve 將Promise對(duì)象的狀態(tài)從pending(進(jìn)行中)變?yōu)閞esolved(已成功)
reject 將Promise對(duì)象的狀態(tài)從pending(進(jìn)行中)變?yōu)閞ejected(已失敗)

其中rejected(已失敗)的狀態(tài)包括程序的錯(cuò)誤也能捕捉并且不會(huì)中斷程序運(yùn)行,并把錯(cuò)誤原因當(dāng)成參數(shù)傳遞下去

const promise = new Promise(function(resolve, reject) {
  //未定義變量
  return num;
});

promise.then(null, function rejected(err) {
  console.log(`rejected:${err}`);
});

//輸出
//rejected: ReferenceError: num is not defined
then

Promise 實(shí)例能訪問(wèn)定義在原型對(duì)象Promise.prototype上的then方法.它的作用是為 Promise 實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù),提供兩個(gè)參數(shù)分別是

參數(shù)名 用途
resolved resolved狀態(tài)的回調(diào)函數(shù)
rejected(可選) 是rejected狀態(tài)的回調(diào)函數(shù),如果發(fā)生錯(cuò)誤但沒(méi)有處理函數(shù)會(huì)程序報(bào)錯(cuò)而不是狀態(tài)失敗報(bào)錯(cuò)
const promise = new Promise(function(resolve, reject) {
  throw "error";
});

//有rejected處理函數(shù)
promise.then(
  function resolved(res) {
    console.log(res);
  },
  function rejected(err) {
    console.log(`捕捉錯(cuò)誤:${err}`);
  }
);

//沒(méi)有rejected處理函數(shù)
promise.then(function resolved(res) {
  console.log(res);
});

//捕捉錯(cuò)誤:error
//系統(tǒng)報(bào)錯(cuò)

因?yàn)閠hen返回的是一個(gè)新的Promise實(shí)例,所以可以鏈?zhǔn)秸{(diào)用,如果需要把當(dāng)前結(jié)果作為下一個(gè)then方法的參數(shù)將它作為返回值return.

const promise = new Promise((resolve, reject) => {
  true ? resolve("success") : reject("error");
});

promise
  .then(function resolved(res) {
    console.log(res);
    //這次不返回結(jié)果了
  })
  .then(function resolved(res) {
    console.log(res);
    //這次返回其他
    return "成功";
  })
  .then(
    function resolved(res) {
      console.log(`成功信息:${res}`);
    },
    function rejected(err) {
      console.log(`失敗信息:${err}`);
    }
  );

//輸出
//success
//undefined
//成功信息:成功

Promise代碼量看著多只是為了讓讀者更直觀,采用ES6箭頭函數(shù)跟縮寫(xiě)名稱可以壓縮成下面這樣

const promise = new Promise ((resolve, reject) => {
  true ? resolve ("success") : reject ("error");
});

promise.then (res => res, err => err);
return返回值

then方法會(huì)隱性返回一個(gè)Promise或者開(kāi)發(fā)者顯性返回任何值,而Promise有自己內(nèi)部機(jī)制處理:

Promise狀態(tài)改變前

如果回調(diào)函數(shù)中返回一個(gè)值或者不返回:

then返回resolved狀態(tài)Promise,并且將返回的值或者undefined作為參數(shù)值.

如果回調(diào)函數(shù)拋出錯(cuò)誤:

then返回rejected狀態(tài)Promise,并且將拋出的錯(cuò)誤作為參數(shù)值.

如果回調(diào)函數(shù)返回一個(gè)Promise:

Promise是pending狀態(tài),then返回pending狀態(tài)Promise,但是兩者的終態(tài)還會(huì)相同的,并且將回調(diào)函數(shù)Promise的參數(shù)值作為它的參數(shù)值;

Promise是resolved狀態(tài),then返回resolved狀態(tài)Promise,并且將回調(diào)函數(shù)Promise的參數(shù)值作為它的參數(shù)值;

Promise是rejected狀態(tài),then返回rejected狀態(tài)Promise,并且將回調(diào)函數(shù)Promise的參數(shù)值作為它的參數(shù)值;

Promise狀態(tài)改變后

前面說(shuō)過(guò)狀態(tài)一旦更改是不可逆,即使之后再添加回調(diào)函數(shù)也只是返回同個(gè)結(jié)果

resolved狀態(tài)

即使后面再拋出錯(cuò)誤then也是返回resolved狀態(tài)Promise;

const promise = new Promise ((resolve, reject) => {
  resolve ();
  throw "error";
});

promise.then (
  res => {
    console.log (`resloved: ${res}`);
  },
  err => {
    console.log (`rejected: ${err}`);
  }
);

//輸出
//resloved: undefined
rejected狀態(tài)

即使后面再顯性返回resolved狀態(tài)Promise也是返回rejected狀態(tài)Promise;

const promise = new Promise ((resolve, reject) => {
  reject ();
  return Promise.resolve ();
});

promise.then (
  res => {
    console.log (`resloved: ${res}`);
  },
  err => {
    console.log (`rejected: ${err}`);
  }
);

//輸出
//rejected: undefined
例子

基本條件如下:

p1設(shè)定三秒后改變狀態(tài)

p2設(shè)定一秒后改變狀態(tài)

p1作為p2的返回參數(shù)傳遞

p1返回resolved狀態(tài),p2返回resolved狀態(tài).
//開(kāi)始執(zhí)行時(shí)間
console.log (`開(kāi)始運(yùn)行時(shí)間: ${new Date ().toTimeString ()}`);
const p1 = new Promise ((resolve, reject) => {
  //3秒后執(zhí)行
  setTimeout (resolve, 3000);
}),
  p2 = new Promise ((resolve, reject) => {
    //1秒后執(zhí)行
    setTimeout (() => resolve (p1), 1000);
  });

//處理時(shí)間
p2.then (
  res => {
    console.log (`p2返回成功狀態(tài)時(shí)間: ${new Date ().toTimeString ()}`);
  },
  err => {
    console.log (`p2返回失敗狀態(tài)時(shí)間: ${new Date ().toTimeString ()}`);
  }
);

//輸出
//開(kāi)始運(yùn)行時(shí)間: 09:27:44 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
//p2返回成功狀態(tài)時(shí)間: 09:27:47 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
p1返回rejectd狀態(tài),p2返回resolved狀態(tài).
//開(kāi)始執(zhí)行時(shí)間
console.log(`開(kāi)始運(yùn)行時(shí)間: ${new Date().toTimeString()}`);
const p1 = new Promise((resolve, reject) => {
    //3秒后執(zhí)行
    setTimeout(reject, 3000);
  }),
  p2 = new Promise((resolve, reject) => {
    //1秒后執(zhí)行
    setTimeout(() => resolve(p1), 1000);
  });

//處理時(shí)間
p2.then(
  res => {
    console.log(`p2返回成功狀態(tài)時(shí)間: ${new Date().toTimeString()}`);
  },
  err => {
    console.log(`p2返回失敗狀態(tài)時(shí)間: ${new Date().toTimeString()}`);
  }
);

//輸出
//開(kāi)始運(yùn)行時(shí)間: 09:30:50 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
//p2返回失敗狀態(tài)時(shí)間: 09:30:53 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
p1返回resolved狀態(tài),p2返回rejectd狀態(tài).
//開(kāi)始執(zhí)行時(shí)間
console.log (`開(kāi)始運(yùn)行時(shí)間: ${new Date ().toTimeString ()}`);
const p1 = new Promise ((resolve, reject) => {
  //3秒后執(zhí)行
  setTimeout (resolve, 3000);
}),
  p2 = new Promise ((resolve, reject) => {
    //1秒后執(zhí)行
    setTimeout (() => reject (p1), 1000);
  });

//處理時(shí)間
p2.then (
  res => {
    console.log (`p2返回成功狀態(tài)時(shí)間: ${new Date ().toTimeString ()}`);
  },
  err => {
    console.log (`p2返回失敗狀態(tài)時(shí)間: ${new Date ().toTimeString ()}`);
  }
);

//輸出
//開(kāi)始運(yùn)行時(shí)間: 09:31:33 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
//p2返回失敗狀態(tài)時(shí)間: 09:31:34 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)

看完三個(gè)例子之后還有一個(gè)省略代碼可以得出結(jié)論:

p1 p2 then 間隔(s)
resolved resolved resolved 3
rejectd resolved rejectd 3
resolved rejectd rejectd 1
rejectd rejectd rejectd 1

結(jié)論:then回調(diào)函數(shù)的Promise狀態(tài)首先取決于調(diào)用函數(shù)(p2)狀態(tài),當(dāng)它resolved(已成功)情況下才取決于返回函數(shù)Promise(p1)的狀態(tài).

catch

如果Promise狀態(tài)變?yōu)閞ejected或者then方法運(yùn)行中拋出錯(cuò)誤,都可以用catch捕捉并且返回一個(gè)新的Promise實(shí)例,建議then方法省略錯(cuò)誤處理,最底層添加一個(gè)catch處理機(jī)制;

const promise = new Promise((resolve, reject) => {
  throw "error";
});

//省略then錯(cuò)誤處理
promise.then().catch(res => {
  console.log(res);
});

//輸出
//error

注意:如果決定使用catch處理的話前面就不能用reject做錯(cuò)誤處理了,因?yàn)楸粩r截之后是不會(huì)再經(jīng)過(guò)catch了

const promise = new Promise((resolve, reject) => {
  throw "error";
});

promise
  .then(null, err => {
    console.log(`then: ${err}`);
    return err;
  })
  .catch(err => {
    //跳過(guò)
    console.log(`catch: ${err}`);
  })
  .then(
    res => {
      console.log(`resloved: ${res}`);
    },
    err => {
      console.log(`rejected: ${err}`);
    }
  );

//輸出
//then: error
//resloved: error

從結(jié)果可以看到第一個(gè)then有了錯(cuò)誤處理函數(shù)之后會(huì)跳過(guò)catch方法,然后第二個(gè)then會(huì)在成功處理函數(shù)里打印?說(shuō)好的狀態(tài)一旦更改是不可逆呢???
這里面又涉及到return值的問(wèn)題了.

第一層then已經(jīng)做了錯(cuò)誤處理,返回一個(gè)resloved狀態(tài)Promise;
因?yàn)閏atch不會(huì)處理resloved狀態(tài)Promise所以跳過(guò);
第二層then接收并在resloved處理函數(shù)處理;

如果記性好的人應(yīng)該記得上面講解例子有個(gè)類似的寫(xiě)法但是卻依然報(bào)錯(cuò),知道原因么?

const promise = new Promise(function(resolve, reject) {
  throw "error";
});

//有rejected處理函數(shù)
promise.then(
  function resolved(res) {
    console.log(res);
  },
  function rejected(err) {
    console.log(`捕捉錯(cuò)誤:${err}`);
  }
);

//沒(méi)有rejected處理函數(shù)
promise.then(function resolved(res) {
  console.log(res);
});

//捕捉錯(cuò)誤:error
//系統(tǒng)報(bào)錯(cuò)

再重復(fù)一遍,Promise每次都會(huì)返回一個(gè)新的Promise實(shí)例,所以不能用鏈?zhǔn)秸{(diào)用后的結(jié)果來(lái)看待變量promise.

靜態(tài)方法 Promise還提供多個(gè)方法控制流程:
方法 作用
resolve 快速返回一個(gè)新的 Promise 對(duì)象,狀態(tài)為resolved的實(shí)例
reject 快速返回一個(gè)新的 Promise 對(duì)象,狀態(tài)為rejected的實(shí)例
finally 不管 Promise 對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作,回調(diào)函數(shù)不接受任何參數(shù)這表明finally方法里面的操作,應(yīng)該是與狀態(tài)無(wú)關(guān)的,不依賴于 Promise 的執(zhí)行結(jié)果。該方法是 ES2018 引入標(biāo)準(zhǔn)的,就不說(shuō)了
all
The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.

大概意思就是當(dāng)?shù)鲄?shù)里的所有Promise都返回成功狀態(tài)或者沒(méi)有Promise入?yún)⒌臅r(shí)候返回一個(gè)成功狀態(tài)Promise,否則返回第一個(gè)失敗狀態(tài)的Promise拋出的錯(cuò)誤.
如果迭代器參數(shù)不是Promise也會(huì)被隱性調(diào)用Promise.resolve方法轉(zhuǎn)成Promis實(shí)例.(Promise.all方法的參數(shù)必須具有 Iterator 接口,且返回的每個(gè)成員都是 Promise 實(shí)例).

//成功
const p1 = new Promise((resolve, reject) => {
    resolve("suc1");
  }),
  //成功
  p2 = new Promise((resolve, reject) => {
    resolve("suc2");
  }),
  //失敗
  p3 = new Promise((resolve, reject) => {
    reject("err");
  });

Promise.all([p1, p2]).then(res => {
  console.log(`全部成功:${res}`);
});

Promise.all([p1, p3]).then(null, err => console.log(`第一個(gè)失敗:${err}`));

Promise.all([]).then(
  res => console.log("空數(shù)組返回成功Promise"),
  err => console.log("空數(shù)組返回失敗Promise")
);

//輸出
//空數(shù)組返回成功Promise
//全部成功:  (2) ["suc1", "suc2"]
//第一個(gè)失敗: err

注意: 如果空數(shù)組情況下立馬返回成功狀態(tài),盡管放在最后位置卻第一個(gè)打印結(jié)果!

race

跟all類似,區(qū)別在于兩點(diǎn):

它只返回第一個(gè)改變Promise狀態(tài)的結(jié)果,不管成功失敗都只返回一個(gè)結(jié)果;

空數(shù)組情況下永遠(yuǎn)不會(huì)執(zhí)行;

//成功
const p1 = new Promise((resolve, reject) => {
    resolve("suc1");
  }),
  //成功
  p2 = new Promise((resolve, reject) => {
    resolve("suc2");
  }),
  //失敗
  p3 = new Promise((resolve, reject) => {
    reject("err");
  });

Promise.race([p1, p2]).then(res => {
  console.log(`第一個(gè)返回成功:${res}`);
});

Promise.race([p3, p2]).then(null, err => console.log(`第一個(gè)返回失敗:${err}`));

Promise.race([]).then(
  res => console.log("空數(shù)組返回成功Promise"),
  err => console.log("空數(shù)組返回失敗Promise")
);

//輸出
//第一個(gè)返回成功:suc1
//第一個(gè)返回失敗:err
使用要點(diǎn)

因?yàn)镻romise的then方法會(huì)把函數(shù)結(jié)果放置到微任務(wù)隊(duì)列(micro tasks),也就是當(dāng)次事件循環(huán)的最后執(zhí)行.如果你使用的是同步函數(shù)實(shí)際上是被無(wú)端延遲執(zhí)行了.如果不清楚這方面內(nèi)容可以再看一下我之前寫(xiě)得Javascript執(zhí)行機(jī)制--單線程,同異步任務(wù),事件循環(huán)

function sync() {
  console.log("sync");
}

function async() {
  setTimeout(() => console.log("async"), 1000);
}

Promise.resolve().then(sync);
Promise.resolve().then(async);
console.log("end");

//輸出
//end
//sync
//async

實(shí)際上有兩種方法可以實(shí)現(xiàn)讓同步函數(shù)同步執(zhí)行,異步函數(shù)異步執(zhí)行,并且讓它們具有統(tǒng)一的 API.

匿名函數(shù)立即執(zhí)行new Promise()
function sync() {
  console.log("sync");
}

function async() {
  setTimeout(() => console.log("async"), 1000);
}

(() => new Promise(resolve => resolve(sync())))();

(() => new Promise(resolve => resolve(async())))();

console.log("end");

// 輸出
// sync
// end
// async
async,如果不知道沒(méi)關(guān)系,下面有講到
function sync() {
  console.log("sync");
}

function async() {
  setTimeout(() => console.log("async"), 1000);
}
(async () => sync())();
(async () => async())();
console.log("end");

// 輸出
// sync
// end
// async
優(yōu)劣點(diǎn) 優(yōu)點(diǎn):

異步流程能使用線性寫(xiě)法鏈?zhǔn)秸{(diào)用,擺脫“回調(diào)地獄”;

多種方式的錯(cuò)誤處理機(jī)制;

能將當(dāng)前結(jié)果往下層層傳遞;

promise是一種規(guī)約,它在回調(diào)調(diào)用和錯(cuò)誤處理規(guī)范化的基礎(chǔ)上給異步編程提供了更多的可能性;

缺點(diǎn):

一旦開(kāi)始無(wú)法停止取消;

盡管可以用catch做統(tǒng)一錯(cuò)誤處理,但是被中間層攔截了之后可能會(huì)造成干擾;

只能知道Promise在實(shí)例pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失敗)狀態(tài)中的一種,沒(méi)有更加詳細(xì)的進(jìn)度信息;

盡管比回調(diào)函數(shù)稍好,但是寫(xiě)法也十分冗余,過(guò)多的then讓代碼語(yǔ)義化不明;

每次都是返回新的Promise實(shí)例,犧牲性能代價(jià)略高;

Generator

Generator 函數(shù)是 ES6 提供的一種異步編程解決方案.

原理

語(yǔ)法上,首先可以把它理解成,Generator 函數(shù)是一個(gè)狀態(tài)機(jī),封裝了多個(gè)內(nèi)部狀態(tài).執(zhí)行 Generator 函數(shù)并不執(zhí)行里面程序而是返回一個(gè)遍歷器對(duì)象,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個(gè)狀態(tài).yield表達(dá)式是暫停執(zhí)行的標(biāo)記,而next方法可以恢復(fù)執(zhí)行.
每一次調(diào)用next方法,都會(huì)返回?cái)?shù)據(jù)結(jié)構(gòu)的當(dāng)前成員的信息.具體來(lái)說(shuō),就是返回一個(gè)包含value和done兩個(gè)屬性的對(duì)象.其中,value屬性是當(dāng)前成員的值,done屬性是一個(gè)布爾值,表示遍歷是否結(jié)束.

特征

function關(guān)鍵字與函數(shù)名之間有一個(gè)星號(hào)就行,哪個(gè)位置都能通過(guò);

函數(shù)體內(nèi)部使用yield表達(dá)式定義不同的內(nèi)部狀態(tài);

function* Generator() {
  yield "1";
  return "2";
}

const example = Generator();
console.log(example);
console.log(example.next());
console.log(example.next());

// 輸出
// Object [Generator] {}
// { value: "1", done: false }
// { value: "2", done: true }
next表達(dá)式運(yùn)行邏輯

yield表達(dá)式本身沒(méi)有返回值,或者說(shuō)總是返回undefined.next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值.否則上一個(gè)yield表達(dá)式的返回值維持原狀不變,這個(gè)特性相當(dāng)重要,我們就依賴它從外部向內(nèi)部注入不同的值,從而調(diào)整函數(shù)行為.調(diào)用next的時(shí)候,內(nèi)部指針會(huì)往下運(yùn)行直到遇到下列情況:

遇到y(tǒng)ield表達(dá)式,它后面的表達(dá)式只有內(nèi)部指針指向該語(yǔ)句時(shí)才會(huì)執(zhí)行,并將它的值作為返回的vaule,然后暫停執(zhí)行;

遇到return表達(dá)式,將它的值作為返回的vaule,遍歷結(jié)束;

運(yùn)行到函數(shù)結(jié)束,將undefined作為返回的vaule,遍歷結(jié)束;

2和3之后仍然可以繼續(xù)調(diào)用next,結(jié)果都是{value: undefined, done: true},即使return后面還有yield表達(dá)式也不會(huì)改變結(jié)果;

然后我們根據(jù)規(guī)則再去解讀那一段代碼的運(yùn)行結(jié)果

function* foo() {
  let x = yield 1;
  console.log(`函數(shù)內(nèi)部x: ${x}`);

  let y = x * (yield x + 2);
  console.log(`函數(shù)內(nèi)部xy: ${x} ${y}`);

  return x + y;
}

const it = foo();
console.log(it.next(2));
console.log(it.next(3));
console.log(it.next(4));

// 輸出
// 函數(shù)內(nèi)部x: undefined
// Object { value: 1, done: false }
// 函數(shù)內(nèi)部x: 3
// Object { value: 5, done: false }
// 函數(shù)內(nèi)部xy: 3 12
// Object { value: 15, done: true }

第一次next: 執(zhí)行var x = yield 1后停止,因?yàn)闆](méi)有上一個(gè)yield表達(dá)式,所以入?yún)?不起效,輸出1;

第二次next: 執(zhí)行yield x + 2后停止,x等于入?yún)?,輸出5;

第三次next: 執(zhí)行return x + y后停止,上一個(gè)yield表達(dá)式var y = 3 * 4(x+2被入?yún)?取代了),輸出15;

概念其實(shí)很清晰,過(guò)程有點(diǎn)復(fù)雜,大家謹(jǐn)記next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值.否則上一個(gè)yield表達(dá)式的返回值維持原狀不變,然后根據(jù)這個(gè)規(guī)則來(lái)算就明白了.

yield* 表達(dá)式運(yùn)行邏輯

Generator函數(shù)內(nèi)部,調(diào)用另一個(gè)Generator函數(shù),默認(rèn)情況下是沒(méi)有效果的,需要用到y(tǒng)ield表達(dá)式,帶*會(huì)返回值,不帶*返回遍歷器對(duì)象.

function* foo() {
  bar(1);
  //內(nèi)部調(diào)用Generator函數(shù)
  yield bar(2);
  yield* bar(3);
  yield 1;
}

function* bar(num) {
  console.log(num);
  yield 2;
}

const it = foo();
console.log(it.next());
console.log(it.next());
console.log(it.next());

// 輸出
// { value: Object [Generator] {}, done: false }
// 3
// { value: 2, done: false }
// { value: 1, done: false }

從輸出結(jié)果可知只有yield * bar(3)生效.
如果被代理的 Generator 函數(shù)有return語(yǔ)句,那么就可以向代理它的Generator函數(shù)返回?cái)?shù)據(jù).

function* genFuncWithReturn() {
  yield "a";
  yield "b";
  return "c";
}

function* logReturned(genObj) {
  const result = yield* genObj;
  //返回值
  console.log(result);
}

const it = logReturned(genFuncWithReturn());
console.log(it.next());
console.log(it.next());
console.log(it.next());

// 輸出
// { value: "a", done: false }
// { value: "b", done: false }
// c
// { value: undefined, done: true }
易錯(cuò)點(diǎn) Generator函數(shù)不用yield表達(dá)式就只剩一個(gè)暫緩執(zhí)行的作用;
function* foo() {
  console.log(1);
  console.log(2);
  return;
}

const it = foo();
console.log(3);
it.next();

// 輸出
// 3
// 1
// 2
非Generator函數(shù)中使用yield表達(dá)式會(huì)句法錯(cuò)誤;
function foo() {
    yield 1;
}

const it = foo();

// 輸出
// Unexpected number
yield表達(dá)式如果用在另一個(gè)表達(dá)式之中,必須放在圓括號(hào)里面,除非用作函數(shù)參數(shù)或放在賦值表達(dá)式的右邊
function* foo() {
  console.log("函數(shù)內(nèi)部x: " + (yield));
  yield 1;
}

const it = foo();
console.log(it.next());
console.log(it.next(3));

// 輸出
// { value: undefined, done: false }
// 函數(shù)內(nèi)部x: 3
// { value: 1, done: false }
原型方法 throw

Generator函數(shù)返回的遍歷器對(duì)象的throw方法(不是全局的throw方法)可以在函數(shù)體外拋出錯(cuò)誤,這意味著出錯(cuò)的代碼與處理錯(cuò)誤的代碼,實(shí)現(xiàn)了時(shí)間和空間上的分離.然后在Generator函數(shù)體內(nèi)被捕獲后,會(huì)附帶執(zhí)行下一條yield表達(dá)式.

const err = function*() {
  try {
    yield;
  } catch (e) {
    console.log(`內(nèi)部捕獲: ${e}`);
  }
};
//執(zhí)行
const it = err();
console.log(it.next());

//外部捕獲錯(cuò)誤
try {
  //兩次拋出錯(cuò)誤
  console.log(it.throw("err1"));
  console.log(it.throw("err2"));
} catch (e) {
  console.log(`外部捕獲: ${e}`);
}

// 輸出
// { value: undefined, done: false }
// 內(nèi)部捕獲 err1
// { value: undefined, done: true }
// 外部捕獲 err2

所以這里實(shí)際執(zhí)行順序it.throw("err1") ? it.next() ? it.throw("err2")
同時(shí),Generator函數(shù)體內(nèi)拋出的錯(cuò)誤,也可以被函數(shù)體外的catch捕獲;

const err = function*() {
  yield "1";
  throw "err";
  yield;
  yield "2";
};
//執(zhí)行
const it = err();
console.log(it.next());

try {
  console.log(it.next());
} catch (e) {
  console.log("外部捕獲", e);
}
//捕捉錯(cuò)誤之后繼續(xù)執(zhí)行
console.log(it.next());

// 輸出
// { value: "1", done: false }
// 外部捕獲 err
// { value: undefined, done: true }

注意最后一次執(zhí)行next函數(shù)返回{value: undefined, done: true},因?yàn)閽伋鲥e(cuò)誤后沒(méi)有做內(nèi)部捕獲,JS引擎會(huì)認(rèn)為這個(gè)Generator函數(shù)已經(jīng)遍歷結(jié)束.

return

返回給定的值或者不傳默認(rèn)undefined,并且終結(jié)遍歷 Generator 函數(shù).

function* Generator() {
  yield "1";
  yield "2";
  yield "3";
  return "4";
}

const it = Generator();
console.log(it.return(2));
console.log(it.next(3));

// 輸出
// { value: 2, done: true }
// { value: undefined, done: true }
實(shí)踐寫(xiě)法
//模擬ajax請(qǐng)求
function ajax() {
  return new Promise((resolve, reject) => {
    setTimeout(
      () =>
        resolve({
          abc: 123,
        }),
      1000
    );
  });
}

function* foo() {
  try {
    yield;
  } catch (err) {
    console.log(`內(nèi)部捕獲: ${err}`);
  }
  yield console.log("再次執(zhí)行");
  yield ajax();
  return res;
}

const it = foo();
it.next();
it.throw("somethings happend");
it.next().value.then(res => {
  console.log(res);
});

// 輸出
// 內(nèi)部捕獲: somethings happend
// 再次執(zhí)行
// { abc: 123 }

暫停執(zhí)行和恢復(fù)執(zhí)行,這是Generator能封裝異步任務(wù)的根本原因,強(qiáng)大的錯(cuò)誤捕捉能力可以在寫(xiě)法上更加自由.
我們可以在上面的代碼擴(kuò)展出更多步驟,例如這種異步依賴的代碼可以以同步順序?qū)懗鰜?lái),或者外層操作res1之后再傳到ajax2等.

function* foo() {
  var res1 = yield ajax1(),
    res2 = yield ajax2(res1);

  return res;
}

盡管Generator異步流程管理非常簡(jiǎn)潔,但是操作流程不算方便,需要開(kāi)發(fā)者決定什么時(shí)候執(zhí)行下一步,甚至你會(huì)發(fā)現(xiàn)在整個(gè)異步流程會(huì)充斥著多個(gè)next的身影.

自動(dòng)遍歷方法

for...of循環(huán)可以自動(dòng)遍歷 Generator 函數(shù)時(shí)生成的Iterator對(duì)象,一旦next方法的返回對(duì)象的done屬性為true,循環(huán)就會(huì)中止,且不包含該返回對(duì)象,所以return語(yǔ)句會(huì)執(zhí)行,但不會(huì)顯示輸出.

function* Generator() {
  yield "1";
  yield "2";
  yield "3";
  return "4";
}

for (let v of Generator()) {
  console.log(v);
}

// 輸出
// 1
// 2
// 3

也可以用while實(shí)現(xiàn),區(qū)別在于for...of循環(huán)返回值,while返回?cái)?shù)據(jù)結(jié)構(gòu)

function* Generator() {
  yield "1";
  yield "2";
  yield "3";
  return "4";
}

let it = Generator(),
  result = it.next();

while (!result.done) {
  console.log(result);
  result = it.next(result);
}

// 輸出
// { value: "1", done: false }
// { value: "2", done: false }
// { value: "3", done: false }
異步衍生庫(kù)co

上面方法不適用于異步操作,如果之間需要依賴關(guān)系的話可能會(huì)導(dǎo)致后續(xù)執(zhí)行失敗,為了實(shí)現(xiàn)這種效果有些很出名的衍生庫(kù)co等,關(guān)鍵源碼很短也比較簡(jiǎn)單,直接貼出來(lái)了.

/**
 * Execute the generator function or a generator
 * and return a promise.
 *
 * @param {Function} fn
 * @return {Promise}
 * @api public
 */

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1);

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
    if (typeof gen === "function") gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== "function") return resolve(gen);

    onFulfilled();

    /**
     * @param {Mixed} res
     * @return {Promise}
     * @api private
     */

    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
      return null;
    }

    /**
     * @param {Error} err
     * @return {Promise}
     * @api private
     */

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    /**
     * Get the next value in the generator,
     * return a promise.
     *
     * @param {Object} ret
     * @return {Promise}
     * @api private
     */

    function next(ret) {
      if (ret.done) return resolve(ret.value);
      var value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(
        new TypeError(
          "You may only yield a function, promise, generator, array, or object, " +
            "but the following object was passed: "" +
            String(ret.value) +
            """
        )
      );
    }
  });
}

完整代碼請(qǐng)看co,具體用法就不說(shuō)了

優(yōu)劣點(diǎn) 優(yōu)點(diǎn)

一種新的異步編程概念,ES6對(duì)協(xié)程的不完全實(shí)現(xiàn),使用同步的方式處理異步操作,改寫(xiě)回調(diào)函數(shù);

出色的控制流管理,Generator內(nèi)部可以使用yield表達(dá)式定義不同的內(nèi)部狀態(tài)并且惰性執(zhí)行;

yield表達(dá)式具備記憶功能,next可以從上一次暫停點(diǎn)開(kāi)始執(zhí)行;

通過(guò)next入?yún)⒑头祷刂悼梢越粨Q數(shù)據(jù)影響執(zhí)行函數(shù);

Generator函數(shù)體外拋出的錯(cuò)誤,可以在函數(shù)體內(nèi)捕獲;反過(guò)來(lái),Generator函數(shù)體內(nèi)拋出的錯(cuò)誤,也可以被函數(shù)體外的catch捕獲;

缺點(diǎn)

相對(duì)復(fù)雜,需要了解更多的知識(shí)點(diǎn),怎么正確使用Generator有很多注意地方,具體詳情自行查看文檔;

流程管理不夠靈活,衍生的co庫(kù)等都有些不足;

犧牲性能代價(jià)比較高;

Async Await

ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),一句話,它就是 Generator 函數(shù)的語(yǔ)法糖.
如果說(shuō) Promise 主要解決的是異步回調(diào)問(wèn)題,那么 async + await 主要解決的就是將異步問(wèn)題同步化,降低異步編程的認(rèn)知負(fù)擔(dān).

特征 將Generator的*換成async,yeild換成await; 內(nèi)置處理器,自動(dòng)執(zhí)行 一旦遇到await就會(huì)先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語(yǔ)句
function p1() {
  return Promise.resolve().then(res => console.log("p1"));
}

function p2() {
  return Promise.resolve().then(res => console.log("p2"));
}

async function Async() {
  console.log(1);
  await p1();
  console.log(2);
  await p2();
}

const it = Async().then(res => console.log(123));

// 輸出
// 1
// p1
// 2
// p2
// 123
如果await命令后面是非Promise對(duì)象會(huì)被強(qiáng)制轉(zhuǎn)換成promise對(duì)象
async function Async() {
  return await 1;
}
Async().then(res => console.log(res));

// 輸出
// 1
如果await命令后面的Promise對(duì)象為reject狀態(tài),就會(huì)中斷整個(gè)Async函數(shù)
async function Async() {
  await Promise.reject("err");
  console.log("不執(zhí)行了");
  await Promise.resolve();
};
Async().catch(err => console.log(err));

// 輸出
// err
使用要點(diǎn) 聯(lián)合Promise.all在遍歷使用
//阻塞主線程
const delazy = (time = 2000) =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, time);
  });

//模擬請(qǐng)求
async function quest() {
  await delazy();
  return Promise.resolve({
    str: "123",
  });
}

//統(tǒng)一獲取
async function doSomethings() {
  const ary = await Promise.all([quest(), quest()]);
  console.log(ary.map(e => e.str));
}

doSomethings();
為了保證不因?yàn)槟硞€(gè)環(huán)節(jié)失敗中斷整個(gè)方法,建議都放在try..catch中
async function Async() {
  try {
    await Promise.reject("err").then(null, err => console.log(err));
    console.log("繼續(xù)執(zhí)行");
    await Promise.resolve("suc").then(res => console.log(res));
  } catch (err) {}
}
Async();

// 輸出
// 繼續(xù)執(zhí)行
// suc
沒(méi)有依賴關(guān)系的方法不要使用await
function p1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000);
  });
}

function p2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(2), 1000);
  });
}

async function sum() {
  const [a, b] = await Promise.all([p1(), p2()]);
  console.log(a, b);
}
==================== OR ============================
async function sum() {
  const a = p1(),
    b = p2(),
    c = await a,
    d = await b;

  console.log(c, d);
}

sum();

// 輸出
// 1 2
總結(jié):

如果想要跳過(guò)錯(cuò)誤繼續(xù)執(zhí)行的解決辦法可以使用try..catch或者catch;

沒(méi)有依賴關(guān)系的異步操作可以直接用await后面接Promise.all()提高性能;

實(shí)現(xiàn)原理(全部代碼源自阮一峰大神的博客)

跟上面說(shuō)的co類似,將Generator函數(shù)和自動(dòng)執(zhí)行器,包裝在一個(gè)函數(shù)里.

async function fn(args) {}
//等價(jià)于
function fn(args) {
  return spawn(function*() {});
}

spawn函數(shù)就是自動(dòng)執(zhí)行器

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

代碼量不多也不難,建議認(rèn)真學(xué)習(xí)里面的思路

優(yōu)劣點(diǎn) 優(yōu)點(diǎn)

內(nèi)置執(zhí)行器,不暴露給用戶;

更好的語(yǔ)義化,更簡(jiǎn)潔的寫(xiě)法;

async函數(shù)的await命令后面,可以是Promise對(duì)象和原始類型的值(數(shù)值、字符串和布爾值,但這時(shí)等同于同步操作);

async函數(shù)的返回值是Promise;

缺點(diǎn)

只要一個(gè)await語(yǔ)句后面的Promise變?yōu)閞eject,那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行;

錯(cuò)誤處理機(jī)制復(fù)雜;

犧牲性能代價(jià)更高;

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/106388.html

相關(guān)文章

  • ES6-7

    摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門(mén),覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評(píng)論0 收藏0
  • 前端_JavaScript

    摘要:為此決定自研一個(gè)富文本編輯器。例如當(dāng)要轉(zhuǎn)化的對(duì)象有環(huán)存在時(shí)子節(jié)點(diǎn)屬性賦值了父節(jié)點(diǎn)的引用,為了關(guān)于函數(shù)式編程的思考作者李英杰,美團(tuán)金融前端團(tuán)隊(duì)成員。只有正確使用作用域,才能使用優(yōu)秀的設(shè)計(jì)模式,幫助你規(guī)避副作用。 JavaScript 專題之惰性函數(shù) JavaScript 專題系列第十五篇,講解惰性函數(shù) 需求 我們現(xiàn)在需要寫(xiě)一個(gè) foo 函數(shù),這個(gè)函數(shù)返回首次調(diào)用時(shí)的 Date 對(duì)象,注意...

    Benedict Evans 評(píng)論0 收藏0
  • 什么是Node.js

    Node.js從2009年誕生至今,已經(jīng)發(fā)展了兩年有余,其成長(zhǎng)的速度有目共睹。從在github的訪問(wèn)量超過(guò)Rails,到去年底Node.jsS創(chuàng)始人Ryan Dalh加盟Joyent獲得企業(yè)資助,再到今年發(fā)布Windows移植版本,Node.js的前景獲得了技術(shù)社區(qū)的肯定。InfoQ一直在關(guān)注Node.js的發(fā)展,在今年的兩次Qcon大會(huì)(北京站和杭州站)都有專門(mén)的講座。為了更好地促進(jìn)Node.j...

    CrazyCodes 評(píng)論0 收藏0
  • JavaScript 異步

    摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫(xiě)一個(gè)符合規(guī)范并可配合使用的寫(xiě)一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...

    tuniutech 評(píng)論0 收藏0
  • [譯]學(xué)習(xí)如何去學(xué)習(xí) JavaScript - 5 個(gè)你應(yīng)該如何花在學(xué)習(xí) JS 上時(shí)間建議

    摘要:擁抱異步編程縱觀發(fā)展史也可以說(shuō)成開(kāi)發(fā)的發(fā)展史,你會(huì)發(fā)現(xiàn)異步徹底改變了這場(chǎng)游戲。可以這么說(shuō),異步編程已成為開(kāi)發(fā)的根基。這也是你應(yīng)盡早在上投入大量時(shí)間的一處核心知識(shí)點(diǎn),這其中包含和等重要概念。這也是最突出的一項(xiàng)貢獻(xiàn)。 原文地址:Medium - Learning How to Learn JavaScript. 5 recommendations on how you should spend ...

    wanglu1209 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<